Skip to content

Commit 53b1572

Browse files
committed
Revert to extends check being part of conditional type
1 parent c10a552 commit 53b1572

7 files changed

Lines changed: 75 additions & 72 deletions

File tree

src/compiler/checker.ts

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2643,10 +2643,11 @@ namespace ts {
26432643
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
26442644
}
26452645
if (type.flags & TypeFlags.Conditional) {
2646-
const conditionTypeNode = typeToTypeNodeHelper((<ConditionalType>type).conditionType, context);
2646+
const checkTypeNode = typeToTypeNodeHelper((<ConditionalType>type).checkType, context);
2647+
const extendsTypeNode = typeToTypeNodeHelper((<ConditionalType>type).extendsType, context);
26472648
const trueTypeNode = typeToTypeNodeHelper((<ConditionalType>type).trueType, context);
26482649
const falseTypeNode = typeToTypeNodeHelper((<ConditionalType>type).falseType, context);
2649-
return createConditionalTypeNode(conditionTypeNode, trueTypeNode, falseTypeNode);
2650+
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
26502651
}
26512652
if (type.flags & TypeFlags.Extends) {
26522653
const leftTypeNode = typeToTypeNodeHelper((<ExtendsType>type).checkType, context);
@@ -3422,7 +3423,11 @@ namespace ts {
34223423
writePunctuation(writer, SyntaxKind.CloseBracketToken);
34233424
}
34243425
else if (type.flags & TypeFlags.Conditional) {
3425-
writeType((<ConditionalType>type).conditionType, TypeFormatFlags.InElementType);
3426+
writeType((<ConditionalType>type).checkType, TypeFormatFlags.InElementType);
3427+
writeSpace(writer);
3428+
writer.writeKeyword("extends");
3429+
writeSpace(writer);
3430+
writeType((<ConditionalType>type).extendsType, TypeFormatFlags.InElementType);
34263431
writeSpace(writer);
34273432
writePunctuation(writer, SyntaxKind.QuestionToken);
34283433
writeSpace(writer);
@@ -6396,14 +6401,11 @@ namespace ts {
63966401
// with its constraint. We do this because if the constraint is a union type it will be distributed
63976402
// over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T'
63986403
// removes 'undefined' from T.
6399-
const conditionType = type.conditionType;
6400-
if (conditionType.flags & TypeFlags.Extends) {
6401-
const checkType = (<ExtendsType>conditionType).checkType;
6402-
if (checkType.flags & TypeFlags.TypeParameter) {
6403-
const constraint = getConstraintOfTypeParameter(<TypeParameter>checkType);
6404-
if (constraint) {
6405-
return instantiateType(type, createTypeMapper([<TypeParameter>checkType], [constraint]));
6406-
}
6404+
const checkType = type.checkType;
6405+
if (checkType.flags & TypeFlags.TypeParameter) {
6406+
const constraint = getConstraintOfTypeParameter(<TypeParameter>checkType);
6407+
if (constraint) {
6408+
return instantiateType(type, createTypeMapper([<TypeParameter>checkType], [constraint]));
64076409
}
64086410
}
64096411
return undefined;
@@ -8387,43 +8389,48 @@ namespace ts {
83878389
return links.resolvedType;
83888390
}
83898391

8390-
function isGenericConditionType(type: Type) {
8391-
return maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive | TypeFlags.Extends);
8392-
}
8393-
8394-
function createConditionalType(conditionType: Type, whenTrueType: Type, whenFalseType: Type, aliasSymbol: Symbol, aliasTypeArguments: Type[]) {
8392+
function createConditionalType(checkType: Type, extendsType: Type, trueType: Type, falseType: Type, aliasSymbol: Symbol, aliasTypeArguments: Type[]) {
83958393
const type = <ConditionalType>createType(TypeFlags.Conditional);
8396-
type.conditionType = conditionType;
8397-
type.trueType = whenTrueType;
8398-
type.falseType = whenFalseType;
8394+
type.checkType = checkType;
8395+
type.extendsType = extendsType;
8396+
type.trueType = trueType;
8397+
type.falseType = falseType;
83998398
type.aliasSymbol = aliasSymbol;
84008399
type.aliasTypeArguments = aliasTypeArguments;
84018400
return type;
84028401
}
84038402

8404-
function getConditionalType(condition: Type, whenTrue: Type, whenFalse: Type, aliasSymbol: Symbol, aliasTypeArguments: Type[], mapper: TypeMapper): Type {
8405-
if (!isGenericConditionType(condition)) {
8406-
return condition.flags & TypeFlags.Never ? neverType : getUnionType([
8407-
typeMaybeAssignableTo(condition, trueType) ? instantiateType(whenTrue, mapper) : neverType,
8408-
typeMaybeAssignableTo(condition, falseType) ? instantiateType(whenFalse, mapper) : neverType]);
8403+
function getConditionalType(checkType: Type, extendsType: Type, mapper: TypeMapper, trueType: Type, falseType: Type, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
8404+
// Distribute union types over conditional types
8405+
if (checkType.flags & TypeFlags.Union) {
8406+
return getUnionType(map((<UnionType>checkType).types, t => getConditionalType(t, extendsType, mapper, trueType, falseType)));
8407+
}
8408+
// Return trueType for a definitely true extends check
8409+
if (isTypeAssignableTo(checkType, extendsType)) {
8410+
return instantiateType(trueType, mapper);
84098411
}
8410-
const resultTrueType = instantiateType(whenTrue, mapper);
8411-
const resultFalseType = instantiateType(whenFalse, mapper);
8412-
const resultTypeArguments = instantiateTypes(aliasTypeArguments, mapper);
8413-
const id = condition.id + "," + resultTrueType.id + "," + resultFalseType.id;
8412+
// Return falseType for a definitely false extends check
8413+
if (!isTypeAssignableTo(instantiateType(checkType, anyMapper), instantiateType(extendsType, constraintMapper))) {
8414+
return instantiateType(falseType, mapper);
8415+
}
8416+
// Otherwise return a deferred conditional type
8417+
const resTrueType = instantiateType(trueType, mapper);
8418+
const resFalseType = instantiateType(falseType, mapper);
8419+
const resTypeArguments = instantiateTypes(aliasTypeArguments, mapper);
8420+
const id = checkType.id + "," + extendsType.id + "," + resTrueType.id + "," + resFalseType.id;
84148421
let type = conditionalTypes.get(id);
84158422
if (!type) {
8416-
conditionalTypes.set(id, type = createConditionalType(condition, resultTrueType, resultFalseType, aliasSymbol, resultTypeArguments));
8423+
conditionalTypes.set(id, type = createConditionalType(checkType, extendsType, resTrueType, resFalseType, aliasSymbol, resTypeArguments));
84178424
}
84188425
return type;
84198426
}
84208427

84218428
function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type {
84228429
const links = getNodeLinks(node);
84238430
if (!links.resolvedType) {
8424-
links.resolvedType = getConditionalType(getTypeFromTypeNode(node.conditionType),
8425-
getTypeFromTypeNode(node.trueType), getTypeFromTypeNode(node.falseType),
8426-
getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node), identityMapper);
8431+
links.resolvedType = getConditionalType(getTypeFromTypeNode(node.checkType), getTypeFromTypeNode(node.extendsType),
8432+
identityMapper, getTypeFromTypeNode(node.trueType), getTypeFromTypeNode(node.falseType),
8433+
getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
84278434
}
84288435
return links.resolvedType;
84298436
}
@@ -9039,23 +9046,19 @@ namespace ts {
90399046
// Check if we have a conditional type of the form T extends U ? X : Y, where T is a type parameter.
90409047
// If so, the conditional type is distributive over a union type and when T is instantiated to a union
90419048
// type A | B, we produce (A extends U ? X : Y) | (B extends U ? X : Y).
9042-
const conditionType = type.conditionType;
9043-
if (conditionType.flags & TypeFlags.Extends) {
9044-
const checkType = (<ExtendsType>conditionType).checkType;
9045-
if (checkType.flags & TypeFlags.TypeParameter) {
9046-
const instantiatedType = mapper(<TypeParameter>checkType);
9047-
if (checkType !== instantiatedType && instantiatedType.flags & TypeFlags.Union) {
9048-
return mapType(instantiatedType, t => instantiateConditionalType(type, createReplacementMapper(checkType, t, mapper)));
9049-
}
9049+
const checkType = type.checkType;
9050+
if (checkType.flags & TypeFlags.TypeParameter) {
9051+
const instantiatedType = mapper(<TypeParameter>checkType);
9052+
if (checkType !== instantiatedType && instantiatedType.flags & TypeFlags.Union) {
9053+
return mapType(instantiatedType, t => instantiateConditionalType(type, createReplacementMapper(checkType, t, mapper)));
90509054
}
90519055
}
90529056
return instantiateConditionalType(type, mapper);
90539057
}
90549058

90559059
function instantiateConditionalType(type: ConditionalType, mapper: TypeMapper): Type {
9056-
return getConditionalType(instantiateType((<ConditionalType>type).conditionType, mapper),
9057-
(<ConditionalType>type).trueType, (<ConditionalType>type).falseType,
9058-
type.aliasSymbol, type.aliasTypeArguments, mapper);
9060+
return getConditionalType(instantiateType(type.checkType, mapper), instantiateType(type.extendsType, mapper),
9061+
mapper, type.trueType, type.falseType, type.aliasSymbol, type.aliasTypeArguments);
90599062
}
90609063

90619064
function instantiateType(type: Type, mapper: TypeMapper): Type {
@@ -11644,7 +11647,8 @@ namespace ts {
1164411647
inferFromTypes((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType);
1164511648
}
1164611649
else if (source.flags & TypeFlags.Conditional && target.flags & TypeFlags.Conditional) {
11647-
inferFromTypes((<ConditionalType>source).conditionType, (<ConditionalType>target).conditionType);
11650+
inferFromTypes((<ConditionalType>source).checkType, (<ConditionalType>target).checkType);
11651+
inferFromTypes((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType);
1164811652
inferFromTypes((<ConditionalType>source).trueType, (<ConditionalType>target).trueType);
1164911653
inferFromTypes((<ConditionalType>source).falseType, (<ConditionalType>target).falseType);
1165011654
}
@@ -20358,7 +20362,6 @@ namespace ts {
2035820362

2035920363
function checkConditionalType(node: ConditionalTypeNode) {
2036020364
forEachChild(node, checkSourceElement);
20361-
checkTypeAssignableTo(getTypeFromTypeNode(node.conditionType), booleanType, node.conditionType);
2036220365
}
2036320366

2036420367
function isPrivateWithinAmbient(node: Node): boolean {

src/compiler/declarationEmitter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,9 @@ namespace ts {
550550
}
551551

552552
function emitConditionalType(node: ConditionalTypeNode) {
553-
emitType(node.conditionType);
553+
emitType(node.checkType);
554+
write(" extends ");
555+
emitType(node.extendsType);
554556
write(" ? ");
555557
emitType(node.trueType);
556558
write(" : ");

src/compiler/emitter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,9 @@ namespace ts {
11341134
}
11351135

11361136
function emitConditionalType(node: ConditionalTypeNode) {
1137-
emit(node.conditionType);
1137+
emit(node.checkType);
1138+
write(" extends ");
1139+
emit(node.extendsType);
11381140
write(" ? ");
11391141
emit(node.trueType);
11401142
write(" : ");

src/compiler/factory.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -720,19 +720,21 @@ namespace ts {
720720
: node;
721721
}
722722

723-
export function createConditionalTypeNode(conditionType: TypeNode, trueType: TypeNode, falseType: TypeNode) {
723+
export function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) {
724724
const node = createSynthesizedNode(SyntaxKind.ConditionalType) as ConditionalTypeNode;
725-
node.conditionType = parenthesizeConditionalTypeMember(conditionType);
725+
node.checkType = parenthesizeConditionalTypeMember(checkType);
726+
node.extendsType = parenthesizeConditionalTypeMember(extendsType);
726727
node.trueType = trueType;
727728
node.falseType = falseType;
728729
return node;
729730
}
730731

731-
export function updateConditionalTypeNode(node: ConditionalTypeNode, conditionType: TypeNode, trueType: TypeNode, falseType: TypeNode) {
732-
return node.conditionType !== conditionType
732+
export function updateConditionalTypeNode(node: ConditionalTypeNode, checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) {
733+
return node.checkType !== checkType
734+
|| node.extendsType !== extendsType
733735
|| node.trueType !== trueType
734736
|| node.falseType !== falseType
735-
? updateNode(createConditionalTypeNode(conditionType, trueType, falseType), node)
737+
? updateNode(createConditionalTypeNode(checkType, extendsType, trueType, falseType), node)
736738
: node;
737739
}
738740

src/compiler/parser.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ namespace ts {
176176
case SyntaxKind.IntersectionType:
177177
return visitNodes(cbNode, cbNodes, (<UnionOrIntersectionTypeNode>node).types);
178178
case SyntaxKind.ConditionalType:
179-
return visitNode(cbNode, (<ConditionalTypeNode>node).conditionType) ||
179+
return visitNode(cbNode, (<ConditionalTypeNode>node).checkType) ||
180+
visitNode(cbNode, (<ConditionalTypeNode>node).extendsType) ||
180181
visitNode(cbNode, (<ConditionalTypeNode>node).trueType) ||
181182
visitNode(cbNode, (<ConditionalTypeNode>node).falseType);
182183
case SyntaxKind.ParenthesizedType:
@@ -2883,23 +2884,13 @@ namespace ts {
28832884
return parseUnionOrIntersectionType(SyntaxKind.UnionType, parseIntersectionTypeOrHigher, SyntaxKind.BarToken);
28842885
}
28852886

2886-
function parseBinaryTypeOrHigher(): TypeNode {
2887-
let type = parseUnionTypeOrHigher();
2888-
while (parseOptional(SyntaxKind.ExtendsKeyword)) {
2889-
const node = <BinaryTypeNode>createNode(SyntaxKind.BinaryType, type.pos);
2890-
node.left = type;
2891-
node.operator = SyntaxKind.ExtendsKeyword;
2892-
node.right = parseUnionTypeOrHigher();
2893-
type = finishNode(node);
2894-
}
2895-
return type;
2896-
}
2897-
28982887
function parseConditionalTypeOrHigher(): TypeNode {
2899-
const type = parseBinaryTypeOrHigher();
2900-
if (parseOptional(SyntaxKind.QuestionToken)) {
2888+
const type = parseUnionTypeOrHigher();
2889+
if (parseOptional(SyntaxKind.ExtendsKeyword)) {
29012890
const node = <ConditionalTypeNode>createNode(SyntaxKind.ConditionalType, type.pos);
2902-
node.conditionType = type;
2891+
node.checkType = type;
2892+
node.extendsType = parseUnionTypeOrHigher();
2893+
parseExpected(SyntaxKind.QuestionToken);
29032894
node.trueType = parseConditionalTypeOrHigher();
29042895
parseExpected(SyntaxKind.ColonToken);
29052896
node.falseType = parseConditionalTypeOrHigher();

src/compiler/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,8 @@ namespace ts {
10991099

11001100
export interface ConditionalTypeNode extends TypeNode {
11011101
kind: SyntaxKind.ConditionalType;
1102-
conditionType: TypeNode;
1102+
checkType: TypeNode;
1103+
extendsType: TypeNode;
11031104
trueType: TypeNode;
11041105
falseType: TypeNode;
11051106
}
@@ -3407,7 +3408,7 @@ namespace ts {
34073408
Intersection = 1 << 18, // Intersection (T & U)
34083409
Index = 1 << 19, // keyof T
34093410
IndexedAccess = 1 << 20, // T[K]
3410-
Conditional = 1 << 21, // C ? T : U
3411+
Conditional = 1 << 21, // T extends U ? X : Y
34113412
Extends = 1 << 22, // T extends U
34123413
/* @internal */
34133414
FreshLiteral = 1 << 23, // Fresh literal or unique type
@@ -3702,7 +3703,8 @@ namespace ts {
37023703
}
37033704

37043705
export interface ConditionalType extends InstantiableType {
3705-
conditionType: Type;
3706+
checkType: Type;
3707+
extendsType: Type;
37063708
trueType: Type;
37073709
falseType: Type;
37083710
}

src/compiler/visitor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,8 @@ namespace ts {
387387

388388
case SyntaxKind.ConditionalType:
389389
return updateConditionalTypeNode(<ConditionalTypeNode>node,
390-
visitNode((<ConditionalTypeNode>node).conditionType, visitor, isTypeNode),
390+
visitNode((<ConditionalTypeNode>node).checkType, visitor, isTypeNode),
391+
visitNode((<ConditionalTypeNode>node).extendsType, visitor, isTypeNode),
391392
visitNode((<ConditionalTypeNode>node).trueType, visitor, isTypeNode),
392393
visitNode((<ConditionalTypeNode>node).falseType, visitor, isTypeNode));
393394

0 commit comments

Comments
 (0)