Skip to content

Long conditional type resolves to any #28663

Closed
@dsherret

Description

@dsherret

TypeScript Version: 3.3.0-dev.20181122

Search Terms: conditional type resolves any

Code

After adding exactly this many conditions, the type of WrappedJsxNodeType will be any.

import * as ts from "typescript";

type WrappedNodeType = CompilerNodeToWrappedType<ts.JsxExpression>;

type CompilerNodeToWrappedType<T extends ts.Node> = T extends ts.ArrayBindingPattern ? ArrayBindingPattern :
    T extends ts.BindingElement ? BindingElement :
    T extends ts.ObjectBindingPattern ? ObjectBindingPattern :
    T extends ts.ClassDeclaration ? ClassDeclaration :
    T extends ts.ClassExpression ? ClassExpression :
    T extends ts.ConstructorDeclaration ? ConstructorDeclaration :
    T extends ts.GetAccessorDeclaration ? GetAccessorDeclaration :
    T extends ts.MethodDeclaration ? MethodDeclaration :
    T extends ts.PropertyDeclaration ? PropertyDeclaration :
    T extends ts.SetAccessorDeclaration ? SetAccessorDeclaration :
    T extends ts.ComputedPropertyName ? ComputedPropertyName :
    T extends ts.Identifier ? Identifier :
    T extends ts.QualifiedName ? QualifiedName :
    T extends ts.SyntaxList ? SyntaxList :
    T extends ts.Decorator ? Decorator :
    T extends ts.JSDoc ? JSDoc :
    T extends ts.JSDocAugmentsTag ? JSDocAugmentsTag :
    T extends ts.JSDocClassTag ? JSDocClassTag :
    T extends ts.JSDocParameterTag ? JSDocParameterTag :
    T extends ts.JSDocPropertyTag ? JSDocPropertyTag :
    T extends ts.JSDocReturnTag ? JSDocReturnTag :
    T extends ts.JSDocTypedefTag ? JSDocTypedefTag :
    T extends ts.JSDocTypeTag ? JSDocTypeTag :
    T extends ts.JSDocUnknownTag ? JSDocUnknownTag :
    T extends ts.EnumDeclaration ? EnumDeclaration :
    T extends ts.EnumMember ? EnumMember :
    T extends ts.AsExpression ? AsExpression :
    T extends ts.AwaitExpression ? AwaitExpression :
    T extends ts.CallExpression ? CallExpression :
    T extends ts.CommaListExpression ? CommaListExpression :
    T extends ts.ConditionalExpression ? ConditionalExpression :
    T extends ts.DeleteExpression ? DeleteExpression :
    T extends ts.ImportExpression ? ImportExpression :
    T extends ts.MetaProperty ? MetaProperty :
    T extends ts.NewExpression ? NewExpression :
    T extends ts.NonNullExpression ? NonNullExpression :
    T extends ts.OmittedExpression ? OmittedExpression :
    T extends ts.ParenthesizedExpression ? ParenthesizedExpression :
    T extends ts.PartiallyEmittedExpression ? PartiallyEmittedExpression :
    T extends ts.PostfixUnaryExpression ? PostfixUnaryExpression :
    T extends ts.PrefixUnaryExpression ? PrefixUnaryExpression :
    T extends ts.SpreadElement ? SpreadElement :
    T extends ts.SuperElementAccessExpression ? SuperElementAccessExpression :
    T extends ts.SuperExpression ? SuperExpression :
    T extends ts.SuperPropertyAccessExpression ? SuperPropertyAccessExpression :
    T extends ts.ThisExpression ? ThisExpression :
    T extends ts.TypeAssertion ? TypeAssertion :
    T extends ts.TypeOfExpression ? TypeOfExpression :
    T extends ts.VoidExpression ? VoidExpression :
    T extends ts.JsxExpression ? JsxExpression : Node<T>;

class Node<NodeType extends ts.Node = ts.Node> {
    compilerNode!: NodeType;
}

class JsxExpression extends Node<ts.JsxExpression> {}
class ArrayBindingPattern extends Node<ts.ArrayBindingPattern> {}
class BindingElement extends Node<ts.BindingElement> {}
class ObjectBindingPattern extends Node<ts.ObjectBindingPattern> {}
class ClassDeclaration extends Node<ts.ClassDeclaration> {}
class ClassExpression extends Node<ts.ClassExpression> {}
class ConstructorDeclaration extends Node<ts.ConstructorDeclaration> {}
class GetAccessorDeclaration extends Node<ts.GetAccessorDeclaration> {}
class MethodDeclaration extends Node<ts.MethodDeclaration> {}
class PropertyDeclaration extends Node<ts.PropertyDeclaration> {}
class SetAccessorDeclaration extends Node<ts.SetAccessorDeclaration> {}
class ComputedPropertyName extends Node<ts.ComputedPropertyName> {}
class Identifier extends Node<ts.Identifier> {}
class QualifiedName extends Node<ts.QualifiedName> {}
class SyntaxList extends Node<ts.SyntaxList> {}
class Decorator extends Node<ts.Decorator> {}
class JSDoc extends Node<ts.JSDoc> {}
class JSDocAugmentsTag extends Node<ts.JSDocAugmentsTag> {}
class JSDocClassTag extends Node<ts.JSDocClassTag> {}
class JSDocParameterTag extends Node<ts.JSDocParameterTag> {}
class JSDocPropertyTag extends Node<ts.JSDocPropertyTag> {}
class JSDocReturnTag extends Node<ts.JSDocReturnTag> {}
class JSDocTypedefTag extends Node<ts.JSDocTypedefTag> {}
class JSDocTypeTag extends Node<ts.JSDocTypeTag> {}
class JSDocUnknownTag extends Node<ts.JSDocUnknownTag> {}
class EnumDeclaration extends Node<ts.EnumDeclaration> {}
class EnumMember extends Node<ts.EnumMember> {}
class AsExpression extends Node<ts.AsExpression> {}
class AwaitExpression extends Node<ts.AwaitExpression> {}
class CallExpression extends Node<ts.CallExpression> {}
class CommaListExpression extends Node<ts.CommaListExpression> {}
class ConditionalExpression extends Node<ts.ConditionalExpression> {}
class DeleteExpression extends Node<ts.DeleteExpression> {}
class ImportExpression extends Node<ts.ImportExpression> {}
class MetaProperty extends Node<ts.MetaProperty> {}
class NewExpression extends Node<ts.NewExpression> {}
class NonNullExpression extends Node<ts.NonNullExpression> {}
class OmittedExpression extends Node<ts.OmittedExpression> {}
class ParenthesizedExpression extends Node<ts.ParenthesizedExpression> {}
class PartiallyEmittedExpression extends Node<ts.PartiallyEmittedExpression> {}
class PostfixUnaryExpression extends Node<ts.PostfixUnaryExpression> {}
class PrefixUnaryExpression extends Node<ts.PrefixUnaryExpression> {}
class SpreadElement extends Node<ts.SpreadElement> {}
class SuperElementAccessExpression extends Node<ts.SuperElementAccessExpression> {}
class SuperExpression extends Node<ts.SuperExpression> {}
class SuperPropertyAccessExpression extends Node<ts.SuperPropertyAccessExpression> {}
class ThisExpression extends Node<ts.ThisExpression> {}
class TypeAssertion extends Node<ts.TypeAssertion> {}
class TypeOfExpression extends Node<ts.TypeOfExpression> {}
class VoidExpression extends Node<ts.VoidExpression> {}

If you remove the first condition in the type alias (T extends ts.ArrayBindingPattern ? ArrayBindingPattern :) you will see it correctly resolve to JsxExpression.

Expected behavior: Resolves to JsxExpression

Actual behavior: Resolves to any.

I was code generating all this with ts-simple-ast until it failed (see here), so I tried randomly shuffling the array of type names I was generating it with to see if what was in the conditions mattered, but it always seemed to fail after 51 classes. (Edit: Updated example to step through the array of nodes and they all fail at 51 classes. Actually, in some cases when shuffling it would differ, but that was just because it happened to match a base type.)

My declaration files keep being generated with any types and occasionally objects I use internally are typed as any because of this issue. Perhaps there's a better way I can do this mapping? Thanks!

Metadata

Metadata

Assignees

Labels

Design LimitationConstraints of the existing architecture prevent this from being fixed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions