-
Notifications
You must be signed in to change notification settings - Fork 14
"default" case for matching #19
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
Conversation
Yep.
I think using
My philosophy is that it's not worth putting in place runtime checks for things that should be caught by the type system. If someone is subverting the type system, they have accepted the risk and responsibility for keeping themselves safe. However, there is a problem in your current implementation, which is that it's no longer type safe. For example, this type checks, but it shouldn't, and will throw unionize({ x: {} }).match({
x: undefined,
default: () => 42
})({ tag: 'x' })
Either way is fine with me, but we'll definitely want to update the readme before releasing. |
|
I would rather this was 2 separate PRs, one for the default case and one for the overloading of |
|
I was trying rly hard to improve typings but I got stuck :( But as It turned out with the current typings you can do exactly the same expect(Foo.match({
x: undefined, // AHA!
y: s => s.length,
})(foo)).toBe(12)So I guess we either try to fix it or keep it as is with Partial<>. But check for undefined before calling. Thoughts? |
|
It is possible for a function to return undefined as well const Foo = unionize({
x: ofType<number>(),
y: ofType<string>()
}, 'tag', 'data')
const foo = Foo.x(3);
// i is inferred as any
const i = Foo.match({
x: n => n + 1,
y: s => undefined, // no errors here
})(foo);Am I missing something? It seems that it is related to tsconfig settings. Works very differently in another project |
|
So the closest typings that I came up with is export type MatchCases<Record, Union, A, K extends keyof Record> =
| Cases<Record, A>
| Cases<Record, A, K> & { default: (variant: Union) => A };
export type Match<Record, Union> = {
<A, K extends keyof Record>(cases: MatchCases<Record, Union, A, K>): (
variant: Union
) => A;
};Which is conceptually either: full keys of Record or any subset + default case. Examples: const Foo = unionize(
{
x: ofType<number>(),
y: ofType<string>()
},
'tag',
'data'
);
const foo = Foo.x(3);
// full set of keys
const a = Foo.match({
x: n => n + 1,
y: s => s.length
})(foo);
// still can put default in here
const a_ = Foo.match({
x: n => n + 1,
y: s => s.length,
default: ({ tag }) => tag.length
})(foo);
// should work but gives an error
const c = Foo.match({ x: n => n + 1, default: _ => 42 })(foo);
// It seems that ts cannot infer 'x' as a subset. That error goes away with explicit type of subset 'x'
const b = Foo.match<number, 'x'>({ x: n => n + 1, default: _ => 42 })(foo);So it is easy to have possible x: undefined using Partial<> and just ignore there values at runtime. I tried a couple of options using ts 2.8 but none seemed to work. This is my 3rd day trying to provide typings and I feel a bit defeated :( |
|
The test file was previously not compiled according to the |
|
posted this question on stackoverflow: https://stackoverflow.com/questions/49545836/typescript-cannot-infer-subset-of-keys-of-object-from-usage |
|
out of all solutions that I tried type EvalCases<Record, Res> =
| Cases<Record, Res> & { else?: never }
| (Partial<Cases<Record, Res>> & { else: (r: Record) => Res });This seems to work the best. Yes you can type {b: undefined} (from stackoverflow) evalMyRecord(rec, {
a: s => s.length,
b: undefined,
else: _ => 2
});But on the other hand all other corner cases seem to be solved. It is also easy to check against undefined values (ts will allow only undefined for 'b') function match(cases: any): (variant: any) => any {
return (variant: any) => {
const k = variant[tagProp]
const handler = cases[k];
// also isn't it faster this way? Try to always extract the prop
return handler !== undefined
? handler(valProp ? variant[valProp] : variant)
: cases.default(variant)
}
} |
|
The latest version uses Partial + check for undefined. Pls take a look |
|
Hey @twop, sorry for the delay, been on vacation. I'll take a look at this tonight. |
|
Thanks, this looks really good! |
This is a step 1 and 1.1 of #18
Concerns/comments: