You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Conditional type doesn't go to true or false branch.Β #48243
typeAction<P,T=never>={type: string;payload: P}&([T]extends[never] ? {metaSkipped: true} : {meta: T});functionprepareAction<P,TextendsRecord<string,unknown>=Record<string,unknown>>(action: Action<P,T>): void{action.meta// Property 'meta' does not exist on type 'Payload<P, T>'.(2339)action.metaSkipped// Property 'metaSkipped' does not exist on type 'Payload<P, T>'.(2339)}
π Actual behavior
properties from intersection are not accessible (conditional type doesn't resolve as true / false branch)
π Expected behavior
conditional type should go to false branch. correct my please if i'm wrong
Resolving of conditional types involving unbound generic type arguments is deferred. At that point the compiler doesn't know what Action<P, T> resolves to, because the compiler doesn't know what types P and T are.
Martin Johns (@MartinJohns) thanks for clarification, but i still don't understand. why it doesn't know what result of conditional type if i'm telling it that T will extend Record<string, unknown>
T can still be never, as never also extends Record<string, unknown>. TypeScript simply does not resolve conditional types involving unbound generic types, even if theoretically in some specific corner cases it could.
if i'm telling it that T will extend Record<string, unknown>
Conditional types are not necessarily linear (meaning that they have predictable behavior between T and an a subtype of T), and figuring out whether or not they are requires reasoning in the form of "Does any type exist such that this type would behave in the other way?", which is not very tractable.
T extends ... distributes over unions. [T] extends [...] does not. The former is thus more likely to be deferred than the latter, since it can't know whether to enable the distributive behavior or not until it has a concrete type to work with.
To be clear, generics in TS don't work like C++ templates. The compiler only looks at the body of a generic once and tries to determine if it will work for all possible combinations of type parameters, but these checks are necessarily conservative because it would be prohibitively expensive and/or complex to do otherwise.
Activity
MartinJohns commentedon Mar 14, 2022
Resolving of conditional types involving unbound generic type arguments is deferred. At that point the compiler doesn't know what
Action<P, T>resolves to, because the compiler doesn't know what typesPandTare.alexeymolchan commentedon Mar 14, 2022
Martin Johns (@MartinJohns) thanks for clarification, but i still don't understand. why it doesn't know what result of conditional type if i'm telling it that T will extend Record<string, unknown>
MartinJohns commentedon Mar 14, 2022
Tcan still benever, asneveralso extendsRecord<string, unknown>. TypeScript simply does not resolve conditional types involving unbound generic types, even if theoretically in some specific corner cases it could.alexeymolchan commentedon Mar 14, 2022
Martin Johns (@MartinJohns) playground but why this works ?
RyanCavanaugh commentedon Mar 14, 2022
Conditional types are not necessarily linear (meaning that they have predictable behavior between
Tand an a subtype ofT), and figuring out whether or not they are requires reasoning in the form of "Does any type exist such that this type would behave in the other way?", which is not very tractable.alexeymolchan commentedon Mar 14, 2022
Ryan Cavanaugh (@RyanCavanaugh) okey, but why [T] extends [never] and T extends never produce different results (you can see the example above) ?
fatcerberus commentedon Mar 14, 2022
Aliaksei Molchan (@alexeymolchan) https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
T extends ...distributes over unions.[T] extends [...]does not. The former is thus more likely to be deferred than the latter, since it can't know whether to enable the distributive behavior or not until it has a concrete type to work with.alexeymolchan commentedon Mar 14, 2022
Bruce Pascoe (@fatcerberus) as i see from playground where
T extends ...no defer occur and i can accessaction.meta. correct me please, if i'm wrong.alexeymolchan commentedon Mar 14, 2022
Martin Johns (@MartinJohns) mapped type like this
type OptionsFlags<Type> = { [Property in keyof Type]: boolean; };will be deferred too ?MartinJohns commentedon Mar 14, 2022
How would the compiler know what the final type will look like without knowing what type
Tactually is?fatcerberus commentedon Mar 14, 2022
To be clear, generics in TS don't work like C++ templates. The compiler only looks at the body of a generic once and tries to determine if it will work for all possible combinations of type parameters, but these checks are necessarily conservative because it would be prohibitively expensive and/or complex to do otherwise.
typescript-bot commentedon Mar 17, 2022
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.