-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conditional type does not narrow union type #29188
Comments
Not 100% positive but I think this might have to do with the way conditional types interact with unions. The conditional type distributes over a union, i.e. it acts like a |
Apparently it can be fixed by reversing the order and adding a second conditional to perform the inference on the right-hand side
To be honest, this simple case doesn't even need a conditional, because you can get the element type of the array by doing |
Conditional types do not produce substitution types for the false branch of the conditional (a.k.a do not narrow in the false branch). There was an attempted fix here: #24821, however this was closed. |
Not sure if this is the same, but the compiler are behaving differently if I have an union of arrays vs array of union: type A = { x: string };
type B = { x: string, y: number };
const arr1: (A | B)[] = [];
arr1.find(e => e.x === ""); // OK
arr1.map(e => e.x); // OK
const arr2: A[] | B[] = [];
arr2.find(e => e.x === ""); // OK
arr2.map(e => e.x); // Error: Cannot invoke an expression whose type lacks a call signature. Shouldn't this |
This is a very different situation, and it is normal that a union of arrays behaves differently to an array of unions in some cases. There probably is some justification that the map should work, however this case involves synthesising a union of call signatures which is pretty complex. I would recommend reading over this PR by @weswigham, #29011, and any related issues. If you still have some queries I think it would be better to start a new issue, or reply to those, as this topic is very different to the initial question in this thread. |
@jack-williams had identified the core of the issue. We use "substitution" types internally to track the constraints applied to a type within the Part of the reason why we didn't move forward with #29011 (other than one of the relations I identified not holding up under scrutiny) is that tracking falsified constraints with substitution types kinda works... but when you perform the substitution, the information is lost, since we do not currently have a concept of a "negated" type (I mitigated this a little bit by remateriaizling the substitutions that tracked negative constraints late, but that's a bit of a hack). We cannot say that the given We regularly bring up how we really do probably need it for completeness, but the complexity "negated" types bring is... large? At least that's what we seem to think - it's not immediately obvious that a So we're very aware of what needs to be done to make this work better... we're just having trouble convincing ourselves that it's "worth it". |
Personally I first read this as “all values which are not string AND not number”—only later switching to a negated “or” to simplify the clause mentally. Maybe that’s just because I’ve been coding too long, though... 😛 I quickly recognize |
Duplicate of #21937. |
Alongside the narrowing mentioned here, Is it possible to add narrowing of union object types using E.g- type Foo = string | { type: string };
type Bar<T extends string> = T;
type Qux<T extends Foo> = {
[K in keyof T]: T[K] extends string ? Bar<T[K]> : T[K] extends { type: string } ? Bar<T[K]['type']> : never;
}; It'd be nicer if this was possible instead- type Qux<T extends Foo> = {
[K in keyof T]: 'type' in T[K] ? Bar<T[K]['type']> : Bar<T[K]>;
}; |
TypeScript Version: 3.2.2
Search Terms: conditional types, unions, narrowing
Code
Expected behavior:
No error. "Leafs" of the
Specification
tree, which have typeArray<T>
(for someT
) should be mapped toA<T>
, while non-leaf properties should be recursively mapped.Actual behavior:
In the right-hand side of the conditional type,
S[key]
is not narrowed toSpecification
, even if the complete type ofS[key]
isArray<any> | Specification
and theArray<any>
case is catched in the left-hand side.Playground Link: link
Related Issues: some similar issues related to conditional types, but I'm not sure whether this is a duplicate of any of them.
The text was updated successfully, but these errors were encountered: