-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Non-naked type parameter match still has distributive behavior #43727
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
Comments
Presumably this is going to turn out to be something like " I feel like I've seen a similar reported issue before but I can't locate it. |
Yeah, the check here is whether the check type is semantically a type parameter, not syntactically. Changing this now just makes it surprising to a different set of people and would be a breaking change, so I think this is working as intended. |
I'm not 100% sure I get what you mean by "semantically" here. I can imagine this having to do with details of when and whether the compiler reduces the checked type to a bare type parameter. For example: type D<T> = (T & unknown) extends true ? "X" : "Y"
type E = D<boolean>; // "Y" | "X", distributive
type F<T> = { prop: T }['prop'] extends true ? "X" : "Y"
type G = F<boolean> // "Y" | "X", distributive
type H<U extends { x: any }> = U['x'] extends true ? "X" : "Y";
type I<T> = H<{ x: T }> // IntelliSense says: T extends true ? "X" : "Y", but:
type J = I<boolean> // "Y", 𝗻𝗼𝘁 distributive The conditional types in I don't necessarily think any of this behavior is a bug, but the only way I can make sense of this in my head is to put aside my conception of semantics and instead focus on order of operations (despite the fact that I only have a dim sense of what this is). |
The relevant code looks like this: function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
const checkType = getTypeFromTypeNode(node.checkType);
const aliasSymbol = getAliasSymbolForTypeNode(node);
const aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol);
const allOuterTypeParameters = getOuterTypeParameters(node, /*includeThisTypes*/ true);
const outerTypeParameters = aliasTypeArguments ? allOuterTypeParameters : filter(allOuterTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, node));
const root: ConditionalRoot = {
node,
checkType,
extendsType: getTypeFromTypeNode(node.extendsType),
isDistributive: !!(checkType.flags & TypeFlags.TypeParameter),
^^^^^^^^^ In In In The distinctions are largely outside the range of what anyone would normally learn, since any form that can immediately reduce to a type parameter is a form that "isn't doing anything" in the first place and thus unlikely to be written. |
I have a question that might sound naive but what's the case for distributivity? I stumbled upon it while coding, it felt like a bug, and I have to use what feels like artificial workarounds like |
Most conditional types benefit from distributivity, e.g. |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
Bug Report
🔎 Search Terms
Generic types, distributive unions, extends, naked type parameter
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
A<T>
inB
distributes types and returns the union of the results.🙂 Expected behavior
Since
T
is not naked inA<T>
, I was expecting distributivity to go away, like in :The text was updated successfully, but these errors were encountered: