Skip to content

Add static method test #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
"name": "Chris Feijoo",
"url": "https://github.com/kube"
},
"contributors": [
{
"name": "Cezary Drożak",
"url": "https://github.com/nawordar"
}
],
"license": "MIT",
"bugs": {
"url": "https://github.com/kube/when-switch/issues"
Expand Down
86 changes: 86 additions & 0 deletions src/__tests__/true.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@

/*#######.
########",#:
#########',##".
##'##'## .##',##.
## ## ## # ##",#.
## ## ## ## ##'
## ## ## :##
## ## ##*/

import when from '../when'
import { StaticCheck, IsType, IsSubtype } from './helpers';

describe("'when.true' syntax with a simple return type", () => {
const getDrinkPrice = (drink: 'Pepsi' | 'Coke' | 'Orangina'): number =>
when
.true(() => drink === 'Coke', 1.5)
.true(() => drink === 'Pepsi', 1.8)
.else(2.0)

StaticCheck<IsType<number, ReturnType<typeof getDrinkPrice>>>()

it('returns value if assertion is true', () => {
expect(getDrinkPrice('Coke')).toEqual(1.5)
expect(getDrinkPrice('Pepsi')).toEqual(1.8)
})

it('returns default value if any of assertions is true', () => {
expect(getDrinkPrice('Orangina')).toEqual(2.0)
})
})

describe("'when.true' syntax with a union return-type", () => {
const getDrinkPrice = (drink: 'Pepsi' | 'Coke' | 'Orangina'): number | string | boolean =>
when
.true(drink === 'Coke', 1.5)
.true(drink === 'Pepsi', true)
.else('Free')

StaticCheck<IsSubtype<number | boolean | string, ReturnType<typeof getDrinkPrice>>>()

it('returns value if matches an expression', () => {
expect(getDrinkPrice('Coke')).toEqual(1.5)
expect(getDrinkPrice('Pepsi')).toEqual(true)
})

it('returns default value if no match', () => {
expect(getDrinkPrice('Orangina')).toEqual('Free')
})
})

describe("'when.true' syntax with a function as `is` return value", () => {
type Action = { type: string }

const apply = (action: Action) =>
when
.true(action.type === 'INCREMENT', () => 2)
.true(action.type === 'DECREMENT', () => true)
.else(() => null)

StaticCheck<IsSubtype<number | boolean | null, ReturnType<typeof apply>>>()

it('returns value if matches an expression', () => {
expect(apply({ type: 'INCREMENT' })).toEqual(2)
expect(apply({ type: 'DECREMENT' })).toEqual(true)
})

it('returns default value if no match', () => {
expect(apply({ type: 'Hello' })).toBeNull()
expect(apply({ type: 'World' })).toBeNull()
})
})

describe("'when.true' syntax with assertion wrapped in thunk", () => {
const isBuffer = (obj: { isBuffer: () => boolean }) =>
when
.true(obj.isBuffer, 1)
.else(2)

StaticCheck<IsSubtype<number, ReturnType<typeof isBuffer>>>()

it('unwraps the thunk', () => {
expect(isBuffer({isBuffer: () => true})).toBe(1)
expect(isBuffer({isBuffer: () => false})).toBe(2)
})
})
40 changes: 36 additions & 4 deletions src/when.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ export type When<T, V> = {
else: <W>(returnValue: ((inputValue: T) => W) | W) => V | W
}

export interface WhenSwitch {
<T>(expr: T): When<T, never>

/** Tests assertion and returns _value_ if assertion is true. */
true: <T>(assertion: (() => boolean) | boolean, value: (() => T) | T) => True<T>
}

export type True<T> = {
true: <U>(assertion: (() => boolean) | boolean, value: (() => U) | U) => True<T | U>,
else: <U>(returnValue: (() => U) | U) => T | U
}

/**
* Exposes same API as `when`, but just propagates a resolved value,
* without doing any further test.
Expand All @@ -39,19 +51,39 @@ const resolve = (resolvedValue: any): When<any, any> => ({
/**
* Tests an object against multiple expressions.
*/
export const when = <T>(expr: T): When<T, never> => ({
export const when = (<T>(expr: T): When<T, never> => ({
is: (constExpr, value) =>
expr === constExpr
? resolve(typeof value === 'function' ? value(constExpr) : value)
? resolve(typeof value === 'function' ? (value as (x: any) => any)(constExpr) : value)
: when(expr),

match: (matcher, value) =>
matcher.test(expr)
? resolve(typeof value === 'function' ? value(expr) : value)
? resolve(typeof value === 'function' ? (value as (x: any) => any)(expr) : value)
: when(expr),

else: defaultValue =>
typeof defaultValue === 'function' ? defaultValue(expr) : defaultValue
typeof defaultValue === 'function' ? (defaultValue as (x: any) => any)(expr) : defaultValue
})) as WhenSwitch

/**
* Exposes same API as `true`, but just propagates a resolved value,
* without doing any further test.
*/
const resolveAssertion = (resolvedValue: any): True<any> => ({
true: () => resolveAssertion(resolvedValue),
else: () => resolvedValue
})

when.true = <T>(assertion: (() => boolean) | boolean, value: (() => T) | T): True<T> =>
(typeof assertion === 'function' ? assertion() : assertion)
? resolveAssertion(typeof value === 'function' ? (value as (() => any))() : value)
: ({
true: when.true,
else: defaultValue =>
typeof defaultValue === 'function'
? (defaultValue as (() => any))()
: defaultValue
})

export default when