| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
Do we have a sense of what kind of libraries would benefit from this? |
Sorry, something went wrong.
|
Probably anyone doing tuple type or template literal type manipulation, plus #48112. I regularly find myself needing to define something like Is or use Extract when inferring from tuple types when I need a more specific type than inference allows. I found a few places that could leverage this:
|
Sorry, something went wrong.
|
You can add the Redux ecosystem (Redux Toolkit, Reselect) to that list - we have a few cases where we have a I'm pretty sure Tanner Linsley (@tannerlinsley) will have quite a few uses in React Table for this, but that's speculation from my side. |
Sorry, something went wrong.
|
I would greatly benefit from this in React Table and React Location where I am already writing a lot branching conditional types to both enforce and extract generics. It’s brittle. This would make those contracts easier to define and much more reliable. |
Sorry, something went wrong.
|
TypeScript Bot (@typescript-bot) pack this |
Sorry, something went wrong.
|
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.
|
Hey Daniel Rosenwasser (@DanielRosenwasser), I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so: {
"devDependencies": {
"typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/121221/artifacts?artifactName=tgz&fileId=9A84662ABF9C9E378556EF91C66AB21BF7E1E2513A39ED6E4C61DFA8C11CBA6302&fileName=/typescript-4.7.0-insiders.20220304.tgz"
}
}
and then running npm install. There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@4.7.0-pr-48112-6".; |
Sorry, something went wrong.
|
In the design meeting we discussed the syntax ambiguity with infer T extends U ?. There are three approaches we can take:
I investigated using something like JS's [In] parser context, however that approach didn't seem viable. The places we would want to allow extends in an infer type (such as in a parenthesized type) are the same places you might want to infer to the check type of a conditional type, so there was no way to separate them using a context flag. |
Sorry, something went wrong.
I have been a bit wary of classifying all cases of ... infer Y ? Y extends Bar ? ... as possible use cases since Y extends Bar ? ... could also indicate the need to distribute over Y rather than merely constraining it. Switching to infer T extends U will definitely need to be assessed on a case-by-case basis. |
Sorry, something went wrong.
Of course, but we only really use it in cases where we have the else case be never - we only really add that inner extends to make the compiler happy. Might not be the core of this PR, but it definitely helps us write code. |
Sorry, something went wrong.
|
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.
Unfortunately, requiring parens alone doesn't resolve the ambiguity, since T extends (infer U extends number ? 1 : 0) ? 1 : 0 is already legal (and thus we could still encounter a trailing ?). Right now I'm leaning towards (2) for its simplicity, but changing the noConditionalTypes parameter of parseTypeWorker into a parser context flag so that we can relax the need for parens in situations where we can be sure a trailing ? wouldn't be ambiguous: // 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 [In], since this still requires speculative parsing, but its reliable and consistent and isn't a breaking change. |
Sorry, something went wrong.
|
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 AStringTuple has a constraint of readonly string[], any elements we infer from it should therefore have a constraint of string"? 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.
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 infer in a template literal type to pull out numeric literals. |
Sorry, something went wrong.
|
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 (A & B) | (C & D), we may now write A & B | C & D. The parens weren't strictly necessary, but they may have made the diagnostics more readable. I can make union/intersection parenthesization more aggressive in general, if necessary, or possibly just pass a more aggressive parenthesizer to the printer for typeToString if we only want it for diagnostics/quickinfo. 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.
|
TypeScript Bot (@typescript-bot) perf test |
Sorry, something went wrong.
|
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.
|
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.
|
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.
|
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.
Sorry, something went wrong.
|
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.
|
Ping Anders Hejlsberg (@ahejlsberg), Wesley Wigham (@weswigham), Nathan Shively-Sanders (@sandersn), Ryan Cavanaugh (@RyanCavanaugh) |
Sorry, something went wrong.
There was a problem hiding this comment.
The parser changes look good but I can't adequately comment on the checker changes.
Sorry, something went wrong.
There was a problem hiding this comment.
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.
I think the latest change should cover this. I reuse getInferredTypeParameterConstraint, and elide the extends clause if the constraint is something we would have inferred from the surrounding context. Constraints inferred from a type argument to a type reference are preserved since the type reference may have been elided during typeToTypeNode. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This adds an optional extends clause to infer T to specify an explicit constraint for the T type parameter. When specified,
the explicit constraint overrides the constraint we would have attempted to infer for T.
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:
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:
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:
In contrast, specifying an explicit constraint via extends allows us to achieve the simpler branching structure of Option 2, while providing additional safety by checking that the constraints are consistent:
Related #48094