Skip to content

Commit

Permalink
feat(match): data pattern narrows data type received
Browse files Browse the repository at this point in the history
closes #95
  • Loading branch information
jasonkuhrt committed Aug 7, 2022
1 parent 3449d71 commit 9785348
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ type PickRecordHavingTag<Tag extends string, ADT extends SomeRecord> = ADT exten
//prettier-ignore
type PreMatcher<ADT extends SomeRecord, Result> = {
[Tag in ADT['_tag']]:
(<ThisResult>(dataPattern: Partial<OmitTag<PickRecordHavingTag<Tag, ADT>>>, handler: (data: PickRecordHavingTag<Tag, ADT>) => ThisResult) => PostMatcher<ADT, never, ThisResult | Result>) &
(<ThisResult, Pattern extends Partial<OmitTag<PickRecordHavingTag<Tag, ADT>>>>(dataPattern: Pattern, handler: (data: Pattern & PickRecordHavingTag<Tag, ADT>, test:Pattern) => ThisResult) => PostMatcher<ADT, never, ThisResult | Result>) &
(<ThisResult>(handler: (data: PickRecordHavingTag<Tag, ADT>) => ThisResult) => PostMatcher<ADT, Tag, ThisResult | Result>)
}

Expand All @@ -143,7 +143,7 @@ type PreMatcher<ADT extends SomeRecord, Result> = {
type PostMatcher<ADT extends SomeRecord, PreviousTagsMatched extends string, Result> = {
[Tag in Exclude<ADT['_tag'], PreviousTagsMatched>]:
(
(<ThisResult>(dataPattern: Partial<OmitTag<PickRecordHavingTag<Tag, ADT>>>, handler: (data: PickRecordHavingTag<Tag, ADT>) => ThisResult) => PostMatcher<ADT, PreviousTagsMatched, '__init__' extends Result ? ThisResult : ThisResult | Result>) &
(<ThisResult, Pattern extends Partial<OmitTag<PickRecordHavingTag<Tag, ADT>>>>(dataPattern: Pattern, handler: (data: Pattern & PickRecordHavingTag<Tag, ADT>) => ThisResult) => PostMatcher<ADT, PreviousTagsMatched, '__init__' extends Result ? ThisResult : ThisResult | Result>) &
(<ThisResult>(handler: (data: PickRecordHavingTag<Tag, ADT>) => ThisResult) => PostMatcher<ADT, Tag|PreviousTagsMatched, ThisResult | Result>)
)
// ^[1] ^[1]
Expand Down
30 changes: 25 additions & 5 deletions tests/match/match.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import { Alge } from '../../src/index.js'
import { SomeRecord } from '../../src/record/types/controller.js'
import { A, a, ab, B, b } from '../__helpers__.js'
import { $A, $AB, $B, A, a, ab, B, b } from '../__helpers__.js'
import { expectType } from 'tsd'
import { z } from 'zod'

it(`is a function`, () => {
expect(typeof Alge.match).toEqual(`function`)
Expand Down Expand Up @@ -71,13 +72,32 @@ describe(`.<tag> (Data Matcher)`, () => {
const builder = Alge.match(a as ab).A({ m: `foo` }, () => 1)
expectType<(dataPattern: a, handler: (data: a) => unknown) => any>(builder.A)
expect(builder.A({ m: `m` }, () => 2).else(null)).toBe(2)
// expectType<(handler: (data: B) => unknown) => any>(builder.B)
// expect(typeof builder.B).toBe(`function`)
})
})
it('_tag is omitted from the data that can be matched on', () => {
// @ts-expect-error _tag is not a valid property
Alge.match(ab).A({ m: '', _tag: 'x' }, () => 1)
// TODO We can manually test that _tag is not expected below but how do we create an automated test?
// We cannot get the parameters it seems because the function is overloaded
Alge.match(ab).A({ m: '' /* , _tag: 'A' <-- not accepted but ignored */ }, () => 1)
})
it('narrows the data based on the match', () => {
const AB = Alge.data($AB)
.record($A)
.schema({ a: z.enum(['a', 'b', 'c']) })
.record($B)
.schema({ b: z.enum(['a', 'b', 'c']) })
.done()
const ab = Math.random() > 0.5 ? AB.A.create({ a: 'a' }) : AB.B.create({ b: 'b' })
const result = Alge.match(ab)
.A({ a: 'c' }, (a) => {
expectType<{ _tag: 'A'; a: 'c' }>(a)
return a
})
.B({ b: 'a' }, (a) => {
expectType<{ _tag: 'B'; b: 'a' }>(a)
return a
})
.else(null)
expectType<null | { _tag: 'B'; b: 'a' } | { _tag: 'A'; a: 'c' }>(result)
})
})

Expand Down

0 comments on commit 9785348

Please sign in to comment.