-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Inline type aliases #30979
base: main
Are you sure you want to change the base?
Inline type aliases #30979
Conversation
cc @DanielRosenwasser @uniqueiniquity and @RyanCavanaugh who briefly got to preview this earlier. |
Without rendering an overall opinion, how about interface Container {
invoke: type TInvoke is () => TInvoke;
} |
I personally feel like that's a bit wordy, since a series of 3 or more non-sigil tokens starts looking more like prose than code; but that's just my own opinion - it might be that that does have nicer aesthetics. (Likewise multiple consecutive sigils is too symbolic; a alternating sequence is what usually looks normal to me) |
Yeah, I'd also prefer to keep it consistent with type alias declarations unless doing so would trample on likely future syntax. |
b32e072
to
aba2c63
Compare
Search term: Named type expression |
+1 I come from #40780. Even this not being the exact the syntax I proposed days ago, I still think this is very necessary for a better DX in more advanced types definitions. Any updates on this? |
I considered something similar several years ago, but using
So, with the syntax I was thinking, the example in the OP might read: interface Container {
invoke: type TInvoke = () => TInvoke, TInvoke;
} But also allows for more complex types like this: // before (uses a type default to define `K`)
type MatchingKeys<TRecord, TMatch, K extends keyof TRecord = keyof TRecord> = K extends (TRecord[K] extends TMatch ? K : never) ? K : never;
// after
type MatchingKeys<TRecord, TMatch> =
type K = keyof TRecord,
K extends (TRecord[K] extends TMatch ? K : never) ? K : never; |
You can almost do this with conditional types and type Is<T extends U, U> = T;
type MatchingKeys<TRecord, TMatch> =
[keyof TRecord] extends [Is<infer K, keyof TRecord>] ?
K extends (TRecord[K] extends TMatch ? K : never) ? K : never
: never; Unfortunately, the |
Yeah, I can imagine allowing that. I'd want the comma expression to be optional, so you don't have to repeat yourself in cases where it's identical like that, but it does seem useful, especially alongside the multi-let form (which would already be comma-separated). |
Without HKTs though, this would be weird: interface Container {
invoke: type TInvoke<U> = () => TInvoke<U>;
} I supposed you'd be forced to instantiate it? interface Container {
invoke: (type TInvoke<U> = () => TInvoke<U>)<unknown>;
} Requiring you to specify a non-type-alias avoids that (even if its repetitive). |
This introduces a new typenode with the syntax:
where within the child
TypeNode
,Identifier
is seen as an in-scope reference to the type represented byTypeNode
. This mostly reuses out existing type alias machinery (with no parse support for generics). This is used along the lines of:This allows anonymous self-referential types to rounttrip thru declaration emit. I'll be spending a bit collecting the related usecases and beefing up the tests, but the implementation is largely done, so I wanted to open a PR to 🚲 🏠 the syntax on. Notable discussion points include:
TypeOperator
)=
be used?type
be used?in
context expressions, akin to a recursivelet
?