@@ -264,6 +264,8 @@ namespace ts {
264264 const intersectionTypes = createMap<IntersectionType>();
265265 const literalTypes = createMap<LiteralType>();
266266 const indexedAccessTypes = createMap<IndexedAccessType>();
267+ const conditionalTypes = createMap<ConditionalType>();
268+ const extendsTypes = createMap<ExtendsType>();
267269 const evolvingArrayTypes: EvolvingArrayType[] = [];
268270 const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
269271
@@ -2621,11 +2623,15 @@ namespace ts {
26212623 return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
26222624 }
26232625 if (type.flags & TypeFlags.Conditional) {
2624- const checkTypeNode = typeToTypeNodeHelper((<ConditionalType>type).checkType, context);
2625- const extendskTypeNode = typeToTypeNodeHelper((<ConditionalType>type).extendsType, context);
2626+ const conditionTypeNode = typeToTypeNodeHelper((<ConditionalType>type).conditionType, context);
26262627 const trueTypeNode = typeToTypeNodeHelper((<ConditionalType>type).trueType, context);
26272628 const falseTypeNode = typeToTypeNodeHelper((<ConditionalType>type).falseType, context);
2628- return createConditionalTypeNode(checkTypeNode, extendskTypeNode, trueTypeNode, falseTypeNode);
2629+ return createConditionalTypeNode(conditionTypeNode, trueTypeNode, falseTypeNode);
2630+ }
2631+ if (type.flags & TypeFlags.Extends) {
2632+ const leftTypeNode = typeToTypeNodeHelper((<ExtendsType>type).checkType, context);
2633+ const rightTypeNode = typeToTypeNodeHelper((<ExtendsType>type).extendsType, context);
2634+ return createBinaryTypeNode(leftTypeNode, SyntaxKind.ExtendsKeyword, rightTypeNode);
26292635 }
26302636
26312637 Debug.fail("Should be unreachable.");
@@ -3396,11 +3402,7 @@ namespace ts {
33963402 writePunctuation(writer, SyntaxKind.CloseBracketToken);
33973403 }
33983404 else if (type.flags & TypeFlags.Conditional) {
3399- writeType((<ConditionalType>type).checkType, TypeFormatFlags.InElementType);
3400- writeSpace(writer);
3401- writer.writeKeyword("extends");
3402- writeSpace(writer);
3403- writeType((<ConditionalType>type).extendsType, TypeFormatFlags.InElementType);
3405+ writeType((<ConditionalType>type).conditionType, TypeFormatFlags.InElementType);
34043406 writeSpace(writer);
34053407 writePunctuation(writer, SyntaxKind.QuestionToken);
34063408 writeSpace(writer);
@@ -3410,6 +3412,13 @@ namespace ts {
34103412 writeSpace(writer);
34113413 writeType((<ConditionalType>type).falseType, TypeFormatFlags.InElementType);
34123414 }
3415+ else if (type.flags & TypeFlags.Extends) {
3416+ writeType((<ExtendsType>type).checkType, TypeFormatFlags.InElementType);
3417+ writeSpace(writer);
3418+ writer.writeKeyword("extends");
3419+ writeSpace(writer);
3420+ writeType((<ExtendsType>type).extendsType, TypeFormatFlags.InElementType);
3421+ }
34133422 else {
34143423 // Should never get here
34153424 // { ... }
@@ -6385,6 +6394,9 @@ namespace ts {
63856394 const falseBaseType = getBaseConstraint((<ConditionalType>t).trueType);
63866395 return trueBaseType && falseBaseType ? getUnionType([trueBaseType, falseBaseType]) : undefined;
63876396 }
6397+ if (t.flags & TypeFlags.Extends) {
6398+ return booleanType;
6399+ }
63886400 if (isGenericMappedType(t)) {
63896401 return emptyObjectType;
63906402 }
@@ -8109,6 +8121,12 @@ namespace ts {
81098121 false;
81108122 }
81118123
8124+ function isGenericConditionType(type: Type): boolean {
8125+ return type.flags & (TypeFlags.TypeVariable | TypeFlags.Extends) ? true :
8126+ type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericConditionType) :
8127+ false;
8128+ }
8129+
81128130 // Return true if the given type is a non-generic object type with a string index signature and no
81138131 // other members.
81148132 function isStringIndexOnlyType(type: Type) {
@@ -8216,33 +8234,73 @@ namespace ts {
82168234 return links.resolvedType;
82178235 }
82188236
8219- function getConditionalType(checkType: Type, extendsType: Type, trueType: Type, falseType: Type, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
8220- if (checkType.flags & TypeFlags.Union) {
8221- return getUnionType(map((<UnionType>checkType).types, t => getConditionalType(t, extendsType, trueType, falseType)),
8222- /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
8223- }
8224- if (isTypeAssignableTo(checkType, extendsType)) {
8225- return trueType;
8226- }
8227- if (!isGenericObjectType(checkType) && !isGenericObjectType(extendsType)) {
8228- return falseType;
8229- }
8237+ function createConditionalType(conditionType: Type, whenTrueType: Type, whenFalseType: Type, aliasSymbol: Symbol, aliasTypeArguments: Type[]) {
82308238 const type = <ConditionalType>createType(TypeFlags.Conditional);
8231- type.checkType = checkType;
8232- type.extendsType = extendsType;
8233- type.trueType = trueType;
8234- type.falseType = falseType;
8239+ type.conditionType = conditionType;
8240+ type.trueType = whenTrueType;
8241+ type.falseType = whenFalseType;
82358242 type.aliasSymbol = aliasSymbol;
82368243 type.aliasTypeArguments = aliasTypeArguments;
82378244 return type;
82388245 }
82398246
8247+ function getConditionalType(condition: Type, whenTrue: Type, whenFalse: Type, aliasSymbol: Symbol, aliasTypeArguments: Type[], mapper: TypeMapper): Type {
8248+ if (!isGenericConditionType(condition)) {
8249+ return condition.flags & TypeFlags.Never ? neverType : getUnionType([
8250+ typeMaybeAssignableTo(condition, trueType) ? instantiateType(whenTrue, mapper) : neverType,
8251+ typeMaybeAssignableTo(condition, falseType) ? instantiateType(whenFalse, mapper) : neverType]);
8252+ }
8253+ const resultTrueType = instantiateType(whenTrue, mapper);
8254+ const resultFalseType = instantiateType(whenFalse, mapper);
8255+ const resultTypeArguments = instantiateTypes(aliasTypeArguments, mapper);
8256+ const id = condition.id + "," + resultTrueType.id + "," + resultFalseType.id;
8257+ let type = conditionalTypes.get(id);
8258+ if (!type) {
8259+ conditionalTypes.set(id, type = createConditionalType(condition, resultTrueType, resultFalseType, aliasSymbol, resultTypeArguments));
8260+ }
8261+ return type;
8262+ }
8263+
82408264 function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type {
82418265 const links = getNodeLinks(node);
82428266 if (!links.resolvedType) {
8243- links.resolvedType = getConditionalType(getTypeFromTypeNode(node.checkType), getTypeFromTypeNode(node.extendsType ),
8267+ links.resolvedType = getConditionalType(getTypeFromTypeNode(node.conditionType ),
82448268 getTypeFromTypeNode(node.trueType), getTypeFromTypeNode(node.falseType),
8245- getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
8269+ getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node), identityMapper);
8270+ }
8271+ return links.resolvedType;
8272+ }
8273+
8274+ function createExtendsType(checkType: Type, extendsType: Type) {
8275+ const type = <ExtendsType>createType(TypeFlags.Extends);
8276+ type.checkType = checkType;
8277+ type.extendsType = extendsType;
8278+ return type;
8279+ }
8280+
8281+ function getExtendsType(checkType: Type, extendsType: Type): Type {
8282+ // sys.write(`getExtendsType(${typeToString(checkType)}, ${typeToString(extendsType)})\n`);
8283+ if (checkType.flags & TypeFlags.Union) {
8284+ return getUnionType(map((<UnionType>checkType).types, t => getExtendsType(t, extendsType)), /*subtypeReduction*/ false);
8285+ }
8286+ if (checkType.flags & TypeFlags.Any) {
8287+ return booleanType;
8288+ }
8289+ if (!isGenericObjectType(checkType) && !isGenericObjectType(extendsType)) {
8290+ return isTypeAssignableTo(checkType, extendsType) ? trueType : falseType;
8291+ }
8292+ const id = checkType.id + "," + extendsType.id;
8293+ let type = extendsTypes.get(id);
8294+ if (!type) {
8295+ extendsTypes.set(id, type = createExtendsType(checkType, extendsType));
8296+ }
8297+ return type;
8298+ }
8299+
8300+ function getTypeFromBinaryTypeNode(node: BinaryTypeNode): Type {
8301+ const links = getNodeLinks(node);
8302+ if (!links.resolvedType) {
8303+ links.resolvedType = getExtendsType(getTypeFromTypeNode(node.left), getTypeFromTypeNode(node.right));
82468304 }
82478305 return links.resolvedType;
82488306 }
@@ -8541,6 +8599,8 @@ namespace ts {
85418599 return getTypeFromMappedTypeNode(<MappedTypeNode>node);
85428600 case SyntaxKind.ConditionalType:
85438601 return getTypeFromConditionalTypeNode(<ConditionalTypeNode>node);
8602+ case SyntaxKind.BinaryType:
8603+ return getTypeFromBinaryTypeNode(<BinaryTypeNode>node);
85448604 // This function assumes that an identifier or qualified name is a type expression
85458605 // Callers should first ensure this by calling isTypeNode
85468606 case SyntaxKind.Identifier:
@@ -8807,20 +8867,23 @@ namespace ts {
88078867 }
88088868
88098869 function getConditionalTypeInstantiation(type: ConditionalType, mapper: TypeMapper): Type {
8810- const checkType = type.checkType;
8811- if (checkType.flags & TypeFlags.TypeParameter) {
8812- const instantiatedType = mapper(<TypeParameter>checkType);
8813- if (checkType !== instantiatedType && instantiatedType.flags & TypeFlags.Union) {
8814- return mapType(instantiatedType, t => instantiateConditionalType(type, createReplacementMapper(checkType, t, mapper)));
8870+ const conditionType = type.conditionType;
8871+ if (conditionType.flags & TypeFlags.Extends) {
8872+ const checkType = (<ExtendsType>conditionType).checkType;
8873+ if (checkType.flags & TypeFlags.TypeParameter) {
8874+ const instantiatedType = mapper(<TypeParameter>checkType);
8875+ if (checkType !== instantiatedType && instantiatedType.flags & TypeFlags.Union) {
8876+ return mapType(instantiatedType, t => instantiateConditionalType(type, createReplacementMapper(checkType, t, mapper)));
8877+ }
88158878 }
88168879 }
88178880 return instantiateConditionalType(type, mapper);
88188881 }
88198882
88208883 function instantiateConditionalType(type: ConditionalType, mapper: TypeMapper): Type {
8821- return getConditionalType(instantiateType((<ConditionalType>type).checkType, mapper), instantiateType((<ConditionalType>type).extendsType , mapper),
8822- instantiateType(( <ConditionalType>type).trueType, mapper), instantiateType(( <ConditionalType>type).falseType, mapper) ,
8823- type.aliasSymbol, instantiateTypes( type.aliasTypeArguments, mapper) );
8884+ return getConditionalType(instantiateType((<ConditionalType>type).conditionType , mapper),
8885+ ( <ConditionalType>type).trueType, ( <ConditionalType>type).falseType,
8886+ type.aliasSymbol, type.aliasTypeArguments, mapper);
88248887 }
88258888
88268889 function instantiateType(type: Type, mapper: TypeMapper): Type {
@@ -8858,6 +8921,9 @@ namespace ts {
88588921 if (type.flags & TypeFlags.Conditional) {
88598922 return getConditionalTypeInstantiation(<ConditionalType>type, mapper);
88608923 }
8924+ if (type.flags & TypeFlags.Extends) {
8925+ return getExtendsType(instantiateType((<ExtendsType>type).checkType, mapper), instantiateType((<ExtendsType>type).extendsType, mapper));
8926+ }
88618927 }
88628928 return type;
88638929 }
@@ -20047,6 +20113,11 @@ namespace ts {
2004720113 checkSourceElement(node.type);
2004820114 }
2004920115
20116+ function checkConditionalType(node: ConditionalTypeNode) {
20117+ forEachChild(node, checkSourceElement);
20118+ checkTypeAssignableTo(getTypeFromTypeNode(node.conditionType), booleanType, node.conditionType);
20119+ }
20120+
2005020121 function isPrivateWithinAmbient(node: Node): boolean {
2005120122 return hasModifier(node, ModifierFlags.Private) && !!(node.flags & NodeFlags.Ambient);
2005220123 }
@@ -23687,6 +23758,11 @@ namespace ts {
2368723758 return checkSourceElement((<ParenthesizedTypeNode | TypeOperatorNode>node).type);
2368823759 case SyntaxKind.TypeOperator:
2368923760 return checkTypeOperator(<TypeOperatorNode>node);
23761+ case SyntaxKind.ConditionalType:
23762+ return checkConditionalType(<ConditionalTypeNode>node);
23763+ case SyntaxKind.BinaryType:
23764+ forEachChild(node, checkSourceElement);
23765+ return;
2369023766 case SyntaxKind.JSDocAugmentsTag:
2369123767 return checkJSDocAugmentsTag(node as JSDocAugmentsTag);
2369223768 case SyntaxKind.JSDocTypedefTag:
0 commit comments