Skip to content

Commit

Permalink
fix(match): properly type return type
Browse files Browse the repository at this point in the history
  • Loading branch information
innocenzi committed Dec 26, 2023
1 parent d7a6b77 commit eb9c9e8
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 8 deletions.
17 changes: 16 additions & 1 deletion src/function.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, it } from 'vitest'
import { expect, expectTypeOf, it } from 'vitest'
import { asyncInvoke, invoke, match, tap, value } from './function'

it('invoke', async() => {
Expand Down Expand Up @@ -67,4 +67,19 @@ it('match', () => {
bar: () => '2',
default: '1',
})).toBe('1')

expect(match(123, {
100: 'foo',
200: 'bar',
default: 'baz',
})).toBe('baz')

expectTypeOf(match('foo', { foo: () => 1, bar: () => 2 })).toBeNumber()
expectTypeOf(match('foo', { foo: () => 'a', bar: () => 'b' })).toBeString()
expectTypeOf(match('foo', { foo: () => 'a', bar: () => 'b', default: () => 'c' })).toBeString()
expectTypeOf(match('foo', { foo: 1, bar: 2 })).toBeNumber()
expectTypeOf(match('foo', { baz: 1, bar: 2, default: 3 })).toBeNumber()
expectTypeOf(match(123, { 123: 'foo', 200: 'bar' })).toBeString()
expectTypeOf(match(123, { 100: 'foo', 200: 'bar', default: 'baz' })).toBeString()
expectTypeOf(match('bar' as string | number, { foo: 'a', default: 'b' })).toMatchTypeOf<string | number>()
})
20 changes: 13 additions & 7 deletions src/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ interface DefaultLookup<TReturnValue> {
default: MaybeCallable<TReturnValue>
}

type Lookup<TValue extends string | number | undefined = string, TReturnValue = unknown> = { [K in NonNullable<TValue>]: MaybeCallable<TReturnValue> }
type Lookup<TValue extends string | number | undefined, TReturnValue> = {
[K in NonNullable<TValue>]: MaybeCallable<TReturnValue>
}

/**
* Matches a value against a lookup table.
Expand All @@ -86,21 +88,25 @@ type Lookup<TValue extends string | number | undefined = string, TReturnValue =
*/
export function match<
TValue extends string | number | undefined,
TReturnValue = unknown,
TLookup extends(Lookup<TValue, TReturnValue> | (Partial<Lookup<TValue, TReturnValue>> & DefaultLookup<TReturnValue>)) = Lookup<TValue, TReturnValue>,
TLookup extends(Lookup<TValue, TReturnValue> | (Partial<Lookup<TValue, TReturnValue>> & DefaultLookup<TReturnValue>)),
TReturnValue,
>(
value: TValue,
lookup: TLookup,
...args: any[]
): TReturnValue {
): TLookup extends Lookup<TValue, infer U>
? U
: TLookup extends (Partial<Lookup<TValue, infer V>> & DefaultLookup<infer V>)
? V
: never {
if (value! in lookup) {
const returnValue = lookup[value!]
const returnValue = lookup[value!] as TLookup[NonNullable<TValue>]
return isFunction(returnValue) ? returnValue(...args) : returnValue
}

if ('default' in lookup) {
const returnValue = lookup.default
return isFunction(returnValue) ? returnValue(...args) : returnValue!
const returnValue = lookup.default as TLookup[NonNullable<TValue>]
return isFunction(returnValue) ? returnValue(...args) : returnValue
}

const handlers = Object.keys(lookup)
Expand Down

0 comments on commit eb9c9e8

Please sign in to comment.