Repository navigation
Type inference when using extend in a generic is not consistentΒ #59993
Description
π Search Terms
type inference when extend condition for a generic as const narrow inconsistent
π Version & Regression Information
- This is the behavior in every version I tried
β― Playground Link
π» Code
declare function f1<T>(x: T): T;
var a1 = f1('Hello'); // string
declare function f2<T extends string>(x: T): T;
var a2 = f2('Hello'); // "Hello" <-- why should the extend change the way type is inferred? How do I use the condition but make it inferred as string?
declare function f3<T extends string | number>(x: T): T;
var a3 = f3('Hello'); // "Hello" <-- this is the same as the above, but show a usecase why I would want this behavior
declare function f4<T extends string[]>(x: T): T;
var a4 = f4(['Hello']); // string[] <-- why suddently it is not inferred as const?
declare function f5<T extends boolean[]>(x: T): T;
var a5 = f5([false]); // false[] // <-- why as cosnt for booleans but not for strings in this use case?π Actual behavior
The type inference is not consistent in the above cases, and it's unclear to me (a) what is the logic behind this, and (b) how do I accomplish a different behavior if I need it. More specifically:
- Why adding
extends stringmeans that the type is now inferred as const? - Why using an array changes the behavior back to not using
as const? - Why
extends string[]andextends boolean[]behaves so differently? Why booleans gets the const treatment while strings don't?
π Expected behavior
Since there is a dedicated feature when we want as const for generics, I think that it should be inferred not as const in all of these use cases. This offers the maximum flexibility for consumers as they can pick the behavior they want.
I would also be happy for any work around for this. Let's take this example:
function f<T extends string | number>(value: T): T;How do I make it so that:
f('somestring')is inferred as stringf<'blue' | 'red'>('red')is inferred as `red'
Also - how do I solve the issue with arrays? Specifically, if I have the function:
function f<T extends any[]>(value: T): T;Can I make it so that f(['string', true]) will be inferred as [string, boolean] instead of [string, true]?
Additional information about the issue
No response
Activity
RyanCavanaugh commentedon Sep 18, 2024
I'm confused because you know about
constbut aren't using it where doing so would give you the result you want.Inference is presented with a set of candidates plus a constraint and tries to figure out what you wanted. This talk goes into some examples - it doesn't talk about literals but you can see from that talk why inference makes some decisions but not others.
Because if you wanted the function to return
stringwhen passed"hello", you wouldn't have written the constraint in the first place.Because the prior argument no longer applies (e.g. you might have a subtype of array that you're specializing to)
Booleans are a union but strings aren't
ShacharHarshuv commentedon Sep 18, 2024
I'm not sure how I'm supposed to get the result I want using
const, as it seems to be the opposite of what I want.Imagine this example:
function f<T extends number | string>(value: T): T. How do I make it inferf('hello')asstring,f(1)as number, and f<'red' | 'blue'>('red') as'red' | 'blue'?In that case, there is a reason to the constraint - I do want to narrow
number | stringbut I want to narrow it to eithernumberorstring.The only solution I was able to figure out is using overloads instead:
However, this doesn't work for more complicated examples. Consider this:
I would expect
f([{name: 'hello', true}])to be inferred as({name: 'hello'}|boolean)[]but it is inferred as({name: 'hello'}|true)[].Is it possible to type in a way that this would work?
typescript-bot commentedon Sep 21, 2024
This issue has been marked as "Question" and has seen no recent activity. It has been automatically closed for house-keeping purposes.