Repository navigation
Functioning NoInfer behavior in generic conditional typesΒ #57050
Copy link
Copy link
Closed as not planned
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Description
π Search Terms
issue #14829
pull #56794 (The pull for 14829)
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
These are conditional type examples in the 2.8 release doc:
type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;
type T10 = Foo<{ a: string; b: string }>; // string
type T11 = Foo<{ a: string; b: number }>; // string | numbertype Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & numberThe proposal is that the such generic types support NoInfer, for example:
type Foo<T> = T extends { a: infer U; b: NoInfer<infer U> } ? U : never;
type T10 = Foo<{ a: string; b: string }>; // string
type T11 = Foo<{ a: string; b: number }>; // (expecting string)type Bar<T> = T extends { a: (x: infer U) => void; b: (x: NoInfer< infer U>) => void }
? U
: never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // (expecting string)π Motivating Example
Testing the latest dev version with pull #56794, these results were obtained:
type Foo<T> = T extends { a: infer U; b: NoInfer<infer U> } ? U : never;
>Foo : Foo<T>
>a : U
>b : NoInfer<U>
type T10 = Foo<{ a: string; b: string }>; // string
>T10 : string
>a : string
>b : string
type T11 = Foo<{ a: string; b: number }>; // never (expecting string)
>T11 : never <- WAS EXPECTING STRING
>a : string
>b : number
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: NoInfer< infer U>) => void }
>Bar : Bar<T>
>a : (x: infer U) => void
>x : U
>b : (x: NoInfer<infer U>) => void
>x : NoInfer<U>
? U
: never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
>T20 : string
>a : (x: string) => void
>x : string
>b : (x: string) => void
>x : string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // (expecting string)
>T21 : never <- WAS EXPECTING STRING
>a : (x: string) => void
>x : string
>b : (x: number) => void
>x : numberThe location marked with <- WAS EXPECTING STRING show results of never where string was expected.
π» Use Cases
- What do you want to use this for?
- generic type inference
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Activity
[-]NoInfer behavior in conditional types[/-][+]Functioniing NoInfer behavior in generic conditional types[/+][-]Functioniing NoInfer behavior in generic conditional types[/-][+]Functioning NoInfer behavior in generic conditional types[/+]ahejlsberg commentedon Jan 16, 2024
Those results are as expected. No inferences are made from the
bproperties, so typestringis inferred from theaproperties. Then, when the inferred typestringis substituted forU, thebproperties fail to match and the conditional types resolve tonever.craigphicks commentedon Jan 16, 2024
So here the "No" in "NoInfer" stands for "No!", as in "Veto". It's not uncoupled.
fatcerberus commentedon Jan 16, 2024
You were expecting
NoInfer<infer U>to act as anunknownin the check clause of the conditional type? If that's the case you could have just written that.NoInfermeans "don't use the type parameter here to guide inference" (soNoInfer<infer U>is an oxymoron); it still gets substituted for a concrete type if it can be inferred from elsewhere.craigphicks commentedon Jan 16, 2024
Bruce Pascoe (@fatcerberus)
It would be nice if you could do that without rewriting the whole type except for the parts you don't want. I will explain that more clearly below.I was wrong again :( Just need toOmitthe troublesome props.craigphicks commentedon Jan 16, 2024
Using the test file below some good and some confusing (only to me) results were obtained:
Without any
NoInferwe get resultsAfter adding
NoInferto the original types iterators, the results become:and then the
UNWANTEDresult has changed toWANTED, but unfortunately the previouslyWANTEDresult has changed toUNWANTED.Finally I can do as Bruce Pascoe (@fatcerberus) wisely suggests, without using
NoInfer-to get
which does indeed work perfectly. And although it is a bit verbose, it is applicable without leaving any trace of
NoInferto clean up later, which might be an advantage anyway.That cleared up my understanding of
NoInfer! Thank you Anders Hejlsberg (@ahejlsberg)! Hopefully this conversation wasn't wasn't a waste and might be even be helpful to others who might think of usingNoInferwhere they should be locally omitting a property before inference, as in the final example.Test File
test file
typescript-bot commentedon Jan 19, 2024
This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes.