Skip to content

Commit 7772caf

Browse files
committed
Make substitution types even if the substitution base isnt a type variable
1 parent b78ef30 commit 7772caf

8 files changed

+173
-26
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4275,7 +4275,7 @@ namespace ts {
42754275
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
42764276
}
42774277
if (type.flags & TypeFlags.Substitution) {
4278-
return typeToTypeNodeHelper((<SubstitutionType>type).typeVariable, context);
4278+
return typeToTypeNodeHelper((<SubstitutionType>type).baseType, context);
42794279
}
42804280

42814281
return Debug.fail("Should be unreachable.");
@@ -11315,17 +11315,15 @@ namespace ts {
1131511315
}
1131611316
symbol = getExpandoSymbol(symbol) || symbol;
1131711317
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
11318-
return getTypeFromClassOrInterfaceReference(node, symbol);
11318+
return getConditionalFlowTypeOfType(getTypeFromClassOrInterfaceReference(node, symbol), node);
1131911319
}
1132011320
if (symbol.flags & SymbolFlags.TypeAlias) {
11321-
return getTypeFromTypeAliasReference(node, symbol);
11321+
return getConditionalFlowTypeOfType(getTypeFromTypeAliasReference(node, symbol), node);
1132211322
}
1132311323
// Get type from reference to named type that cannot be generic (enum or type parameter)
1132411324
const res = tryGetDeclaredTypeOfSymbol(symbol);
1132511325
if (res) {
11326-
return checkNoTypeArguments(node, symbol) ?
11327-
res.flags & TypeFlags.TypeParameter ? getConstrainedTypeVariable(<TypeParameter>res, node) : getRegularTypeOfLiteralType(res) :
11328-
errorType;
11326+
return checkNoTypeArguments(node, symbol) ? getConditionalFlowTypeOfType(getRegularTypeOfLiteralType(res), node) : errorType;
1132911327
}
1133011328
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
1133111329
const jsdocType = getTypeFromJSDocValueReference(node, symbol);
@@ -11335,7 +11333,7 @@ namespace ts {
1133511333
else {
1133611334
// Resolve the type reference as a Type for the purpose of reporting errors.
1133711335
resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type);
11338-
return getTypeOfSymbol(symbol);
11336+
return getConditionalFlowTypeOfType(getTypeOfSymbol(symbol), node);
1133911337
}
1134011338
}
1134111339
return errorType;
@@ -11373,17 +11371,17 @@ namespace ts {
1137311371
return links.resolvedJSDocType;
1137411372
}
1137511373

11376-
function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) {
11377-
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === typeVariable) {
11378-
return typeVariable;
11374+
function getSubstitutionType(baseType: Type, substitute: Type) {
11375+
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === baseType) {
11376+
return baseType;
1137911377
}
11380-
const id = `${getTypeId(typeVariable)}>${getTypeId(substitute)}`;
11378+
const id = `${getTypeId(baseType)}>${getTypeId(substitute)}`;
1138111379
const cached = substitutionTypes.get(id);
1138211380
if (cached) {
1138311381
return cached;
1138411382
}
1138511383
const result = <SubstitutionType>createType(TypeFlags.Substitution);
11386-
result.typeVariable = typeVariable;
11384+
result.baseType = baseType;
1138711385
result.substitute = substitute;
1138811386
substitutionTypes.set(id, result);
1138911387
return result;
@@ -11393,25 +11391,25 @@ namespace ts {
1139311391
return node.kind === SyntaxKind.TupleType && (<TupleTypeNode>node).elementTypes.length === 1;
1139411392
}
1139511393

11396-
function getImpliedConstraint(typeVariable: TypeVariable, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined {
11397-
return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(typeVariable, (<TupleTypeNode>checkNode).elementTypes[0], (<TupleTypeNode>extendsNode).elementTypes[0]) :
11398-
getActualTypeVariable(getTypeFromTypeNode(checkNode)) === typeVariable ? getTypeFromTypeNode(extendsNode) :
11394+
function getImpliedConstraint(type: Type, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined {
11395+
return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(type, (<TupleTypeNode>checkNode).elementTypes[0], (<TupleTypeNode>extendsNode).elementTypes[0]) :
11396+
getActualTypeVariable(getTypeFromTypeNode(checkNode)) === type ? getTypeFromTypeNode(extendsNode) :
1139911397
undefined;
1140011398
}
1140111399

11402-
function getConstrainedTypeVariable(typeVariable: TypeVariable, node: Node) {
11400+
function getConditionalFlowTypeOfType(type: Type, node: Node) {
1140311401
let constraints: Type[] | undefined;
1140411402
while (node && !isStatement(node) && node.kind !== SyntaxKind.JSDocComment) {
1140511403
const parent = node.parent;
1140611404
if (parent.kind === SyntaxKind.ConditionalType && node === (<ConditionalTypeNode>parent).trueType) {
11407-
const constraint = getImpliedConstraint(typeVariable, (<ConditionalTypeNode>parent).checkType, (<ConditionalTypeNode>parent).extendsType);
11405+
const constraint = getImpliedConstraint(type, (<ConditionalTypeNode>parent).checkType, (<ConditionalTypeNode>parent).extendsType);
1140811406
if (constraint) {
1140911407
constraints = append(constraints, constraint);
1141011408
}
1141111409
}
1141211410
node = parent;
1141311411
}
11414-
return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable;
11412+
return constraints ? getSubstitutionType(type, getIntersectionType(append(constraints, type))) : type;
1141511413
}
1141611414

1141711415
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
@@ -12854,7 +12852,7 @@ namespace ts {
1285412852
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
1285512853
(<IndexedAccessType>resolved).objectType === objectType &&
1285612854
(<IndexedAccessType>resolved).indexType === indexType ?
12857-
getConstrainedTypeVariable(<IndexedAccessType>resolved, node) : resolved;
12855+
getConditionalFlowTypeOfType(resolved, node) : resolved;
1285812856
}
1285912857
return links.resolvedType;
1286012858
}
@@ -12876,7 +12874,7 @@ namespace ts {
1287612874

1287712875
function getActualTypeVariable(type: Type): Type {
1287812876
if (type.flags & TypeFlags.Substitution) {
12879-
return (<SubstitutionType>type).typeVariable;
12877+
return (<SubstitutionType>type).baseType;
1288012878
}
1288112879
if (type.flags & TypeFlags.IndexedAccess && (
1288212880
(<IndexedAccessType>type).objectType.flags & TypeFlags.Substitution ||
@@ -12984,7 +12982,7 @@ namespace ts {
1298412982
node,
1298512983
checkType,
1298612984
extendsType: getTypeFromTypeNode(node.extendsType),
12987-
trueType: getTypeFromTypeNode(node.trueType),
12985+
trueType: getConditionalFlowTypeOfType(getTypeFromTypeNode(node.trueType), node.trueType),
1298812986
falseType: getTypeFromTypeNode(node.falseType),
1298912987
isDistributive: !!(checkType.flags & TypeFlags.TypeParameter),
1299012988
inferTypeParameters: getInferTypeParameters(node),
@@ -13959,7 +13957,7 @@ namespace ts {
1395913957
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
1396013958
}
1396113959
if (flags & TypeFlags.Substitution) {
13962-
const maybeVariable = instantiateType((<SubstitutionType>type).typeVariable, mapper);
13960+
const maybeVariable = instantiateType((<SubstitutionType>type).baseType, mapper);
1396313961
if (maybeVariable.flags & TypeFlags.TypeVariable) {
1396413962
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((<SubstitutionType>type).substitute, mapper));
1396513963
}
@@ -14968,7 +14966,7 @@ namespace ts {
1496814966
const t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
1496914967
getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? createTypeReference((<TypeReference>type).target, getTypeArguments(<TypeReference>type)) :
1497014968
type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) :
14971-
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).typeVariable : (<SubstitutionType>type).substitute :
14969+
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).baseType : (<SubstitutionType>type).substitute :
1497214970
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
1497314971
type;
1497414972
if (t === type) break;

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4808,7 +4808,7 @@ namespace ts {
48084808
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution
48094809
// types disappear upon instantiation (just like type parameters).
48104810
export interface SubstitutionType extends InstantiableType {
4811-
typeVariable: TypeVariable; // Target type variable
4811+
baseType: Type; // Target type
48124812
substitute: Type; // Type to substitute for type parameter
48134813
}
48144814

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2523,7 +2523,7 @@ declare namespace ts {
25232523
resolvedFalseType: Type;
25242524
}
25252525
export interface SubstitutionType extends InstantiableType {
2526-
typeVariable: TypeVariable;
2526+
baseType: Type;
25272527
substitute: Type;
25282528
}
25292529
export enum SignatureKind {

tests/baselines/reference/api/typescript.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2523,7 +2523,7 @@ declare namespace ts {
25232523
resolvedFalseType: Type;
25242524
}
25252525
export interface SubstitutionType extends InstantiableType {
2526-
typeVariable: TypeVariable;
2526+
baseType: Type;
25272527
substitute: Type;
25282528
}
25292529
export enum SignatureKind {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [inlineConditionalHasSimilarAssignability.ts]
2+
type MyExtract<T, U> = T extends U ? T : never
3+
4+
function foo<T>(a: T) {
5+
const b: Extract<any[], T> = 0 as any;
6+
a = b; // ok
7+
8+
const c: (any[] extends T ? any[] : never) = 0 as any;
9+
a = c;
10+
11+
const d: MyExtract<any[], T> = 0 as any;
12+
a = d; // ok
13+
14+
type CustomType = any[] extends T ? any[] : never;
15+
const e: CustomType = 0 as any;
16+
a = e;
17+
}
18+
19+
//// [inlineConditionalHasSimilarAssignability.js]
20+
function foo(a) {
21+
var b = 0;
22+
a = b; // ok
23+
var c = 0;
24+
a = c;
25+
var d = 0;
26+
a = d; // ok
27+
var e = 0;
28+
a = e;
29+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
=== tests/cases/compiler/inlineConditionalHasSimilarAssignability.ts ===
2+
type MyExtract<T, U> = T extends U ? T : never
3+
>MyExtract : Symbol(MyExtract, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 0))
4+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 15))
5+
>U : Symbol(U, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 17))
6+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 15))
7+
>U : Symbol(U, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 17))
8+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 15))
9+
10+
function foo<T>(a: T) {
11+
>foo : Symbol(foo, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 46))
12+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
13+
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
14+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
15+
16+
const b: Extract<any[], T> = 0 as any;
17+
>b : Symbol(b, Decl(inlineConditionalHasSimilarAssignability.ts, 3, 7))
18+
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
19+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
20+
21+
a = b; // ok
22+
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
23+
>b : Symbol(b, Decl(inlineConditionalHasSimilarAssignability.ts, 3, 7))
24+
25+
const c: (any[] extends T ? any[] : never) = 0 as any;
26+
>c : Symbol(c, Decl(inlineConditionalHasSimilarAssignability.ts, 6, 7))
27+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
28+
29+
a = c;
30+
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
31+
>c : Symbol(c, Decl(inlineConditionalHasSimilarAssignability.ts, 6, 7))
32+
33+
const d: MyExtract<any[], T> = 0 as any;
34+
>d : Symbol(d, Decl(inlineConditionalHasSimilarAssignability.ts, 9, 7))
35+
>MyExtract : Symbol(MyExtract, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 0))
36+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
37+
38+
a = d; // ok
39+
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
40+
>d : Symbol(d, Decl(inlineConditionalHasSimilarAssignability.ts, 9, 7))
41+
42+
type CustomType = any[] extends T ? any[] : never;
43+
>CustomType : Symbol(CustomType, Decl(inlineConditionalHasSimilarAssignability.ts, 10, 8))
44+
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
45+
46+
const e: CustomType = 0 as any;
47+
>e : Symbol(e, Decl(inlineConditionalHasSimilarAssignability.ts, 13, 7))
48+
>CustomType : Symbol(CustomType, Decl(inlineConditionalHasSimilarAssignability.ts, 10, 8))
49+
50+
a = e;
51+
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
52+
>e : Symbol(e, Decl(inlineConditionalHasSimilarAssignability.ts, 13, 7))
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/compiler/inlineConditionalHasSimilarAssignability.ts ===
2+
type MyExtract<T, U> = T extends U ? T : never
3+
>MyExtract : MyExtract<T, U>
4+
5+
function foo<T>(a: T) {
6+
>foo : <T>(a: T) => void
7+
>a : T
8+
9+
const b: Extract<any[], T> = 0 as any;
10+
>b : Extract<any[], T>
11+
>0 as any : any
12+
>0 : 0
13+
14+
a = b; // ok
15+
>a = b : Extract<any[], T>
16+
>a : T
17+
>b : Extract<any[], T>
18+
19+
const c: (any[] extends T ? any[] : never) = 0 as any;
20+
>c : any[] extends T ? any[] : never
21+
>0 as any : any
22+
>0 : 0
23+
24+
a = c;
25+
>a = c : any[] extends T ? any[] : never
26+
>a : T
27+
>c : any[] extends T ? any[] : never
28+
29+
const d: MyExtract<any[], T> = 0 as any;
30+
>d : MyExtract<any[], T>
31+
>0 as any : any
32+
>0 : 0
33+
34+
a = d; // ok
35+
>a = d : MyExtract<any[], T>
36+
>a : T
37+
>d : MyExtract<any[], T>
38+
39+
type CustomType = any[] extends T ? any[] : never;
40+
>CustomType : any[] extends T ? any[] : never
41+
42+
const e: CustomType = 0 as any;
43+
>e : any[] extends T ? any[] : never
44+
>0 as any : any
45+
>0 : 0
46+
47+
a = e;
48+
>a = e : any[] extends T ? any[] : never
49+
>a : T
50+
>e : any[] extends T ? any[] : never
51+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
type MyExtract<T, U> = T extends U ? T : never
2+
3+
function foo<T>(a: T) {
4+
const b: Extract<any[], T> = 0 as any;
5+
a = b; // ok
6+
7+
const c: (any[] extends T ? any[] : never) = 0 as any;
8+
a = c;
9+
10+
const d: MyExtract<any[], T> = 0 as any;
11+
a = d; // ok
12+
13+
type CustomType = any[] extends T ? any[] : never;
14+
const e: CustomType = 0 as any;
15+
a = e;
16+
}

0 commit comments

Comments
 (0)