GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||
GeezerVison is showing github.com — Add 'extends' clause to 'infer' type by rbuckton · Pull Request #48112 · microsoft/TypeScript Conversation
Contributor
This adds an optional For example, if you wished to infer a type from the first element of a tuple but also constrain that type, you might currently need to write something like the following: // This type alias serves as an example of any type with a constraint you might need to satisfy type MustBeNumber<T extends number> = { y: T }; // Option 1, use a nested conditional type X1<T> = T extends [infer U] ? U extends number ? MustBeNumber<U> : never : never; // Option 2, use a secondary type to constrain the inference type Is<T extends U, U> = T; type X2<T> = T extends [Is<infer U, number>] ? MustBeNumber<U> : never; For Option 1, you are forced to create two conditional types, resulting in two alternatives. If you are testing multiple other conditions, this could result in an unmanageable branching structure which results in the need to define additional type aliases for repeated branches: type X1<T> = T extends [infer U] ? U extends number ? MustBeNumber<U> : T extends {x: infer U} ? U extends number ? MustBeNumber<U> : ... // repeats... T extends {x: infer U} ? U extends number ? MustBeNumber<U> : ... For Option 2, you are required to define a second type to enforce the constraint. While this is trivial, it is unreliable when inferring to the same type variable in more than one position, as it is possible to define disjoint constraints: type X2<T> = T extends [Is<infer U, number>, Is<infer U, string>] ? MustBeNumber<U> : never; // U will always be `never` In contrast, specifying an explicit constraint via // simpler branching due to combining the constraint type X3<T> = T extends [infer U extends number] ? MustBeNumber<U> : never; // all constraints for same type parameter must match, if present type X4<T> = T extends [infer U extends number, infer U extends number] ? MustBeNumber<U> : never; // only one type parameter needs to be constrained (similar to class/interface merging) type X5<T> = T extends [infer U extends number, infer U] ? MustBeNumber<U> : never; type X6<T> = T extends [infer U, infer U extends number] ? MustBeNumber<U> : never; // errors on inconsistent constraints type X7<T> = T extends [infer U extends string, infer U extends number] ? U : never; // error: All declarations of 'U' must have identical constraints. Related #48094
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
Ron Buckton (rbuckton)
requested review from
Anders Hejlsberg (ahejlsberg) and
Wesley Wigham (weswigham)
Member
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page.
Contributor
Author
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page.
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page.
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page.
Ron Buckton (rbuckton)
force-pushed
the
inferTypeExtends
branch
from
695e440 to
bafe193
Compare
Member
All reactions
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page.
|
There was an error while loading. Please reload this page.
|
Heya Daniel Rosenwasser (@DanielRosenwasser), I've started to run the tarball bundle task on this PR at bafe193. You can monitor the build here. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Hey Daniel Rosenwasser (@DanielRosenwasser), I've packed this into an installable tgz. You can install it for testing by referencing it in your and then running There is also a playground for this build and an npm module you can use via |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
In the design meeting we discussed the syntax ambiguity with
I investigated using something like JS's |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
I have been a bit wary of classifying all cases of |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Of course, but we only really use it in cases where we have the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I'd also toss in "require parenthesization" as a possible parsing strategy type X<T> = T extends (infer U extends number) ? 1 : 0; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Unfortunately, requiring parens alone doesn't resolve the ambiguity, since Right now I'm leaning towards (2) for its simplicity, but changing the // ok, parsed as conditional type X1<T> = T extends ((infer U) extends number ? 1 : 0) ? 1 : 0; // ok, parsed as `infer..extends` (speculative parse succeeds due to no trailing `?`) type X2<T> = T extends (infer U extends number) ? 1 : 0; // ok, parsed as `infer..extends` (conditional types not allowed in 'extends type' of conditional) type X3<T> = T extends infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (precedence wouldn't have parsed the `?` as part of a type operator) type X4<T> = T extends keyof infer U extends number ? 1 : 0; // ok, parsed as conditional (speculative parse rewinds when it sees the first `?`) type X5<T> = T extends { [P in infer U extends keyof T ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) type X6<T> = T extends { [P in infer U extends keyof T]: 1; } ? 1 : 0; // ok, parsed as conditional (speculative parse rewinds when it sees the first `?`) type X7<T> = T extends { [P in keyof T as infer U extends P ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (speculative parse succeeds due to no trailing `?`) type X8<T> = T extends { [P in keyof T as infer U extends P]: 1; } ? 1 : 0; It doesn't work quite the same way as |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I'm gonna go ahead and ask the dumb question: Why can't we automatically derive the constraint of an inferred type binding from the constraint of the type being matched? For example, given type Quote<AString extends string> = `"${AString}"`; type QuotedFirstElement<AStringTuple extends readonly string[]> = AStringTuple extends readonly [infer FirstElement, ...infer Rest] ? Quote<FirstElement> // Currently errors here, because `FirstElement` is only constrained to `unknown` : never; why can't we say "well, since Intuitively, I suspect there's a very good reason for this and that I just haven't thought it through enough, but someone's gotta spring the trap! 😁 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
We do have automatic inference, and there are ways we can improve it. However, that still wouldn't solve the issue at hand. There are times where you need a more-specific type than what the inferred constraint might allow: type X<T extends any[]> = T extends [infer U extends string, ...infer R] ? ... : T extends [infer U extends number, ...infer R] ? ... : ...; Above, I might want to make branching decisions based on a more specific constraint. This also ties into #48094, which uses more specific inferences for |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
This last change modifies the parse based on #48112 (comment). I wanted to ensure that parenthesization was accurate, so I took a pass through the parenthesizer rule logic for types to be more accurate. One side-effect of that is that we'll emit fewer parens in declarations and diagnostics. Where we might have previously written Daniel Rosenwasser (@DanielRosenwasser), can you provide your thoughts on the change in diagnostics evidenced here: 38eb412?show-viewed-files=true&file-filters%5B%5D=#diff-daa0fded8957f3b2eecdd68e6f89377663f965eb6e9c2b83f36955200b6e858fL5-R6 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
TypeScript Bot (@typescript-bot) perf test |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
38eb412 to
2fb093c
Compare
There was an error while loading. Please reload this page.
|
Heya Ron Buckton (@rbuckton), I've started to run the extended test suite on this PR at 2fb093c. You can monitor the build here. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Heya Ron Buckton (@rbuckton), I've started to run the parallelized Definitely Typed test suite on this PR at 2fb093c. You can monitor the build here. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Heya Ron Buckton (@rbuckton), I've started to run the parallelized community code test suite on this PR at 2fb093c. You can monitor the build here. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Heya Ron Buckton (@rbuckton), I've started to run the perf test suite on this PR at 2fb093c. You can monitor the build here. Update: The results are in! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
1731ad3 to
326389b
Compare
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Heya Ron Buckton (@rbuckton), I've started to run the parallelized Definitely Typed test suite on this PR at 326389b. You can monitor the build here. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Ping Anders Hejlsberg (@ahejlsberg), Wesley Wigham (@weswigham), Nathan Shively-Sanders (@sandersn), Ryan Cavanaugh (@RyanCavanaugh) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The parser changes look good but I can't adequately comment on the checker changes.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
typeToTypeNodeHelper in the checker needs to include the constraint in the context.inferTypeParameters case of type.flags & TypeFlags.TypeParameter. If it's not there, we'll elide the constraints in error messages and inferred type declaration emit (eg, if one of these is inlined into a function return type).
Other than that, looks good - pretty simple.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
I think the latest change should cover this. I reuse |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
2e4fa4f to
01b9a2d
Compare
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.