-
Notifications
You must be signed in to change notification settings - Fork 35
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
[feature request] switch
and pattern match in type position
#1686
Comments
This looks like a great idea for addition! There are a few details that I'm not quite sure of, though your approach might be best:
|
I'm a big fan of improving the usability of type level programming in Civet and happy to support any PR in this direction. It looks like a good overall approach with lots of compelling examples. As Erik mentions there are some nuances to work out but nothing insurmountable. Thanks for the thoughtful and detailed request! |
Hi Civet team! Thanks for your prompt response and great consideration! I do think most decisions here are trade-offs and more design taste than correctness. I will shed several thoughts on it but I respect all your decision.
Exactly, all conditions in types will need extends so it almost always needs a
However, I speculate most TypeScript users will need the plain
That sounds great. I think the compilation can be swapping the types in true/false positions. (Note users may not use
If Civet choose this approach, the above React Query example will become
I think this may be not as concise as the original example, because it is very common to use outer scope type in match. |
I wonder if we could allow for these with some kind of placeholder syntax. For example: switch T
[ & ] < [ Bound ]
// [ T ] extends [ Bound ]
Equal &, Foo < true
// Equal<T, Foo> extends true
Equal &, Foo
// Possible shorthand for above, given that it has & so is clearly a condition
// unless cyclic conditions like `T extends Equal<T, Foo>` are meaningful?
[&] < [Foo] and [Foo] < [&]
// if we want to be ambitious; alternatively, could make a helper This is a natural extension of a previous proposal to allow functions as checkers in value pattern matching. |
I have |
Agreed, DeepUnwrapRef<T> ::= switch T
UnwrapLeaf then T
Ref U then DeepUnwrapRef U
{} then { [Property in keyof T]: DeepUnwrapRef T[Property] }
else UnwrapRef T We could write This might be clearer if we wrote |
This list a good unambiguous addition to pattern matching. And this also reminds me that switch
[ T ] < [ Bound ]
Equal T, Foo < true
[T] < [Foo] and [Foo] < [T] If we model the switch T
& < Bound then A # using placeholder
< Bound2 then B # optional placeholder
Bound3 then C # optional extends operator I would argue here that one single What may be confusing is using bound inside object/array pattern. Consider switch T
[Head, ...Tails] then ???
{ project: Project } then ??? It is not clear to me if the type variable should be pinned or not. |
Hi, thanks for building Civet. The if/else addition to the conditional type greatly increases the language expressiveness.
Since Civet also supports runtime
switch
and pattern matching, here is a suggestion for adding them to type level. The inspiration also comes from Scala's match type.This is a fan post so feel free to close.
Motivating Example
It is common to check the type of one type parameter several times in a type alias. While Civet's
if/else
extension helps reduce long ternary type's verbosity, it can be further simplified byswitch
.Consider this type, from Civet's playground
can be simplified using switch with pattern matching
More Examples and Behaviors
Basic Behavior
Runtime switch expression can be compiled down to a series of if conditionals, so is type level switch.
is equivalent to this Civet
Using literal, tuple, object
The example above has already shown switching on literal. We can also support other types.
Note the last type
[a, b, ...c]
is equivalent to TypeScript's[a, b, ...c]
. Type position pattern match is different from value position pattern match.Using type name and bind
Since using existing type name is more common in conditional type, it is better to reference outer scope variable as default. (this is different from switch expression in value position)
is equivalent to
It is possible to create a new binding in type level pattern matching using
^
is equivalent to
This is similar to the runtime value switch pattern matching.
Binding can also be used with object restructuring and
name^bound
can also be used asinfer name extends bound
syntax in TypeScript.compiles to
Using template literal type
Real World Example
Take React-Query as an example.
can be compile down to
It is arguably more readable than the original type
Alignment with Civet Design Philosopy
The text was updated successfully, but these errors were encountered: