-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Nested Exclude
has unexpected behavior
#28824
Comments
I've run into a similar (likely related) issue, probably also having to do with distribution over const f = <U extends Exclude<any, Function>>(a: U | ( () => U )): U => {
if (typeof a === "function" ) {
return a(); // Cannot invoke an expression whose type lacks a call signature.
// Type '(() => U) | (U & Function)' has no compatible call signatures.
}
return a;
}; My understanding is that |
@riggs I'm confused by what type NonFunction<U> = U extends Function ? never : U; Ya looking at this I'm not really sure how I would make sense of it so I'm not sure what hope the compiler has. |
From type Exclude<T, U> = T extends U ? never : T; |
I think this is a smaller repro: type Union = 'a' | 'b';
type Z<K extends Union> = false extends ([Union] extends [K] ? never : false) ? 'LEFT' : 'RIGHT'; If you hover over The issue is that the conditional type is getting marked as non-deferred and it seems to be eagerly resolving using the constraint of // If this is a distributive conditional type and the check type is generic we need to defer
// resolution of the conditional type such that a later instantiation will properly distribute
// over union types.
const isDeferred = root.isDistributive && maybeTypeOfKind(checkType, TypeFlags.Instantiable); The actual branch that is getting selected is later: // Return trueType for a definitely true extends check. The definitely assignable relation excludes
// type variable constraints from consideration. Without the definitely assignable relation, the type
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number
// would immediately resolve to 'string' instead of being deferred.
if (checkTypeRelatedTo(checkType, inferredExtendsType, definitelyAssignableRelation, /*errorNode*/ undefined)) {
return instantiateType(root.trueType, combinedMapper || mapper);
} A candidate fix is: const isDeferred = maybeTypeOfKind(extendsType, TypeFlags.Instantiable)
|| root.isDistributive && maybeTypeOfKind(checkType, TypeFlags.Instantiable); but I think this is probably incomplete and there are other cases to deal with. @weswigham I think you have an open PR that would fix this, right? (#27932) |
Pardon the side-track: Why wrap |
Essentially the compiler is trying to eagerly resolve the conditional type using wildcard types, substituting type parameters for (I think). |
Amazing turnaround. Thanks @ahejlsberg and team. |
TypeScript Version:
Search Terms:
unexpected, exclude
Code
Expected behavior:
I would expect the parametrized and non-parametrized type to work the same way but that's not
the case.
Actual behavior:
See the comments.
Playground Link: link
Related Issues: Some issues related to
Pick
andExclude
but didn't seem to apply.The text was updated successfully, but these errors were encountered: