Skip to content

Commit 4dd1e2f

Browse files
authored
Ensure instantiation expressions have symbols, preventing crash in signature relations (#56064)
1 parent 649e614 commit 4dd1e2f

13 files changed

+283
-15
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6838,6 +6838,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
68386838
const typeId = type.id;
68396839
const symbol = type.symbol;
68406840
if (symbol) {
6841+
const isInstantiationExpressionType = !!(getObjectFlags(type) & ObjectFlags.InstantiationExpressionType);
6842+
if (isInstantiationExpressionType) {
6843+
const instantiationExpressionType = type as InstantiationExpressionType;
6844+
const existing = instantiationExpressionType.node;
6845+
if (isTypeQueryNode(existing) && getTypeFromTypeNode(existing) === type) {
6846+
const typeNode = serializeExistingTypeNode(context, existing);
6847+
if (typeNode) {
6848+
return typeNode;
6849+
}
6850+
}
6851+
if (context.visitedTypes?.has(typeId)) {
6852+
return createElidedInformationPlaceholder(context);
6853+
}
6854+
return visitAndTransformType(type, createTypeNodeFromObjectType);
6855+
}
68416856
const isInstanceType = isClassInstanceSide(type) ? SymbolFlags.Type : SymbolFlags.Value;
68426857
if (isJSConstructor(symbol.valueDeclaration)) {
68436858
// Instance and static types share the same symbol; only add 'typeof' for the static side.
@@ -6869,20 +6884,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
68696884
}
68706885
}
68716886
else {
6872-
const isInstantiationExpressionType = !!(getObjectFlags(type) & ObjectFlags.InstantiationExpressionType);
6873-
if (isInstantiationExpressionType) {
6874-
const instantiationExpressionType = type as InstantiationExpressionType;
6875-
if (isTypeQueryNode(instantiationExpressionType.node)) {
6876-
const typeNode = serializeExistingTypeNode(context, instantiationExpressionType.node);
6877-
if (typeNode) {
6878-
return typeNode;
6879-
}
6880-
}
6881-
if (context.visitedTypes?.has(typeId)) {
6882-
return createElidedInformationPlaceholder(context);
6883-
}
6884-
return visitAndTransformType(type, createTypeNodeFromObjectType);
6885-
}
68866887
// Anonymous types without a symbol are never circular.
68876888
return createTypeNodeFromObjectType(type);
68886889
}
@@ -19542,6 +19543,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1954219543
}
1954319544

1954419545
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): AnonymousType {
19546+
Debug.assert(type.symbol, "anonymous type must have symbol to be instantiated");
1954519547
const result = createObjectType(type.objectFlags & ~(ObjectFlags.CouldContainTypeVariablesComputed | ObjectFlags.CouldContainTypeVariables) | ObjectFlags.Instantiated, type.symbol) as AnonymousType;
1954619548
if (type.objectFlags & ObjectFlags.Mapped) {
1954719549
(result as MappedType).declaration = (type as MappedType).declaration;
@@ -23074,6 +23076,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2307423076
// method). Simply do a pairwise comparison of the signatures in the two signature lists instead
2307523077
// of the much more expensive N * M comparison matrix we explore below. We erase type parameters
2307623078
// as they are known to always be the same.
23079+
Debug.assertEqual(sourceSignatures.length, targetSignatures.length);
2307723080
for (let i = 0; i < targetSignatures.length; i++) {
2307823081
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, intersectionState, incompatibleReporter(sourceSignatures[i], targetSignatures[i]));
2307923082
if (!related) {
@@ -35677,7 +35680,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3567735680
hasSignatures ||= resolved.callSignatures.length !== 0 || resolved.constructSignatures.length !== 0;
3567835681
hasApplicableSignature ||= callSignatures.length !== 0 || constructSignatures.length !== 0;
3567935682
if (callSignatures !== resolved.callSignatures || constructSignatures !== resolved.constructSignatures) {
35680-
const result = createAnonymousType(/*symbol*/ undefined, resolved.members, callSignatures, constructSignatures, resolved.indexInfos) as ResolvedType & InstantiationExpressionType;
35683+
const result = createAnonymousType(createSymbol(SymbolFlags.None, InternalSymbolName.InstantiationExpression), resolved.members, callSignatures, constructSignatures, resolved.indexInfos) as ResolvedType & InstantiationExpressionType;
3568135684
result.objectFlags |= ObjectFlags.InstantiationExpressionType;
3568235685
result.node = node;
3568335686
return result;

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5955,6 +5955,7 @@ export const enum InternalSymbolName {
59555955
ExportEquals = "export=", // Export assignment symbol
59565956
Default = "default", // Default export symbol (technically not wholly internal, but included here for usability)
59575957
This = "this",
5958+
InstantiationExpression = "__instantiationExpression", // Instantiation expressions
59585959
}
59595960

59605961
/**
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
aliasInstantiationExpressionGenericIntersectionNoCrash1.ts(10,1): error TS2352: Conversion of type '{ new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)' to type '{ new (): ErrImpl<string>; prototype: ErrImpl<any>; } & (() => string)' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
2+
Type '{ new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)' is not comparable to type '{ new (): ErrImpl<string>; prototype: ErrImpl<any>; }'.
3+
Type 'ErrImpl<number>' is not comparable to type 'ErrImpl<string>'.
4+
Type 'number' is not comparable to type 'string'.
5+
6+
7+
==== aliasInstantiationExpressionGenericIntersectionNoCrash1.ts (1 errors) ====
8+
class ErrImpl<E> {
9+
e!: E;
10+
}
11+
12+
declare const Err: typeof ErrImpl & (<T>() => T);
13+
14+
type ErrAlias<U> = typeof Err<U>;
15+
16+
declare const e: ErrAlias<number>;
17+
e as ErrAlias<string>;
18+
~~~~~~~~~~~~~~~~~~~~~
19+
!!! error TS2352: Conversion of type '{ new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)' to type '{ new (): ErrImpl<string>; prototype: ErrImpl<any>; } & (() => string)' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
20+
!!! error TS2352: Type '{ new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)' is not comparable to type '{ new (): ErrImpl<string>; prototype: ErrImpl<any>; }'.
21+
!!! error TS2352: Type 'ErrImpl<number>' is not comparable to type 'ErrImpl<string>'.
22+
!!! error TS2352: Type 'number' is not comparable to type 'string'.
23+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash1.ts] ////
2+
3+
//// [aliasInstantiationExpressionGenericIntersectionNoCrash1.ts]
4+
class ErrImpl<E> {
5+
e!: E;
6+
}
7+
8+
declare const Err: typeof ErrImpl & (<T>() => T);
9+
10+
type ErrAlias<U> = typeof Err<U>;
11+
12+
declare const e: ErrAlias<number>;
13+
e as ErrAlias<string>;
14+
15+
16+
//// [aliasInstantiationExpressionGenericIntersectionNoCrash1.js]
17+
"use strict";
18+
var ErrImpl = /** @class */ (function () {
19+
function ErrImpl() {
20+
}
21+
return ErrImpl;
22+
}());
23+
e;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash1.ts] ////
2+
3+
=== aliasInstantiationExpressionGenericIntersectionNoCrash1.ts ===
4+
class ErrImpl<E> {
5+
>ErrImpl : Symbol(ErrImpl, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 0))
6+
>E : Symbol(E, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 14))
7+
8+
e!: E;
9+
>e : Symbol(ErrImpl.e, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 18))
10+
>E : Symbol(E, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 14))
11+
}
12+
13+
declare const Err: typeof ErrImpl & (<T>() => T);
14+
>Err : Symbol(Err, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 13))
15+
>ErrImpl : Symbol(ErrImpl, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 0))
16+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 38))
17+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 38))
18+
19+
type ErrAlias<U> = typeof Err<U>;
20+
>ErrAlias : Symbol(ErrAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 49))
21+
>U : Symbol(U, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 6, 14))
22+
>Err : Symbol(Err, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 13))
23+
>U : Symbol(U, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 6, 14))
24+
25+
declare const e: ErrAlias<number>;
26+
>e : Symbol(e, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 8, 13))
27+
>ErrAlias : Symbol(ErrAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 49))
28+
29+
e as ErrAlias<string>;
30+
>e : Symbol(e, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 8, 13))
31+
>ErrAlias : Symbol(ErrAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 49))
32+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash1.ts] ////
2+
3+
=== aliasInstantiationExpressionGenericIntersectionNoCrash1.ts ===
4+
class ErrImpl<E> {
5+
>ErrImpl : ErrImpl<E>
6+
7+
e!: E;
8+
>e : E
9+
}
10+
11+
declare const Err: typeof ErrImpl & (<T>() => T);
12+
>Err : typeof ErrImpl & (<T>() => T)
13+
>ErrImpl : typeof ErrImpl
14+
15+
type ErrAlias<U> = typeof Err<U>;
16+
>ErrAlias : { new (): ErrImpl<U>; prototype: ErrImpl<any>; } & (() => U)
17+
>Err : typeof ErrImpl & (<T>() => T)
18+
19+
declare const e: ErrAlias<number>;
20+
>e : { new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)
21+
22+
e as ErrAlias<string>;
23+
>e as ErrAlias<string> : { new (): ErrImpl<string>; prototype: ErrImpl<any>; } & (() => string)
24+
>e : { new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)
25+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
aliasInstantiationExpressionGenericIntersectionNoCrash2.ts(15,1): error TS2352: Conversion of type 'Wat<number>' to type 'Wat<string>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
2+
Type 'Wat<number>' is not comparable to type '{ new (): Class<string>; prototype: Class<any>; }'.
3+
Type 'Class<number>' is not comparable to type 'Class<string>'.
4+
Type 'number' is not comparable to type 'string'.
5+
6+
7+
==== aliasInstantiationExpressionGenericIntersectionNoCrash2.ts (1 errors) ====
8+
declare class Class<T> {
9+
x: T;
10+
}
11+
12+
declare function fn<T>(): T;
13+
14+
15+
type ClassAlias<T> = typeof Class<T>;
16+
type FnAlias<T> = typeof fn<T>;
17+
18+
type Wat<T> = ClassAlias<T> & FnAlias<T>;
19+
20+
21+
declare const wat: Wat<number>;
22+
wat as Wat<string>;
23+
~~~~~~~~~~~~~~~~~~
24+
!!! error TS2352: Conversion of type 'Wat<number>' to type 'Wat<string>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
25+
!!! error TS2352: Type 'Wat<number>' is not comparable to type '{ new (): Class<string>; prototype: Class<any>; }'.
26+
!!! error TS2352: Type 'Class<number>' is not comparable to type 'Class<string>'.
27+
!!! error TS2352: Type 'number' is not comparable to type 'string'.
28+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash2.ts] ////
2+
3+
//// [aliasInstantiationExpressionGenericIntersectionNoCrash2.ts]
4+
declare class Class<T> {
5+
x: T;
6+
}
7+
8+
declare function fn<T>(): T;
9+
10+
11+
type ClassAlias<T> = typeof Class<T>;
12+
type FnAlias<T> = typeof fn<T>;
13+
14+
type Wat<T> = ClassAlias<T> & FnAlias<T>;
15+
16+
17+
declare const wat: Wat<number>;
18+
wat as Wat<string>;
19+
20+
21+
//// [aliasInstantiationExpressionGenericIntersectionNoCrash2.js]
22+
"use strict";
23+
wat;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash2.ts] ////
2+
3+
=== aliasInstantiationExpressionGenericIntersectionNoCrash2.ts ===
4+
declare class Class<T> {
5+
>Class : Symbol(Class, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 0))
6+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 20))
7+
8+
x: T;
9+
>x : Symbol(Class.x, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 24))
10+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 20))
11+
}
12+
13+
declare function fn<T>(): T;
14+
>fn : Symbol(fn, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 2, 1))
15+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 20))
16+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 20))
17+
18+
19+
type ClassAlias<T> = typeof Class<T>;
20+
>ClassAlias : Symbol(ClassAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 28))
21+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 16))
22+
>Class : Symbol(Class, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 0))
23+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 16))
24+
25+
type FnAlias<T> = typeof fn<T>;
26+
>FnAlias : Symbol(FnAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 37))
27+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 13))
28+
>fn : Symbol(fn, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 2, 1))
29+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 13))
30+
31+
type Wat<T> = ClassAlias<T> & FnAlias<T>;
32+
>Wat : Symbol(Wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 31))
33+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 10, 9))
34+
>ClassAlias : Symbol(ClassAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 28))
35+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 10, 9))
36+
>FnAlias : Symbol(FnAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 37))
37+
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 10, 9))
38+
39+
40+
declare const wat: Wat<number>;
41+
>wat : Symbol(wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 13, 13))
42+
>Wat : Symbol(Wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 31))
43+
44+
wat as Wat<string>;
45+
>wat : Symbol(wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 13, 13))
46+
>Wat : Symbol(Wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 31))
47+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash2.ts] ////
2+
3+
=== aliasInstantiationExpressionGenericIntersectionNoCrash2.ts ===
4+
declare class Class<T> {
5+
>Class : Class<T>
6+
7+
x: T;
8+
>x : T
9+
}
10+
11+
declare function fn<T>(): T;
12+
>fn : <T>() => T
13+
14+
15+
type ClassAlias<T> = typeof Class<T>;
16+
>ClassAlias : typeof Class<T>
17+
>Class : typeof Class
18+
19+
type FnAlias<T> = typeof fn<T>;
20+
>FnAlias : typeof fn<T>
21+
>fn : <T_1>() => T_1
22+
23+
type Wat<T> = ClassAlias<T> & FnAlias<T>;
24+
>Wat : Wat<T>
25+
26+
27+
declare const wat: Wat<number>;
28+
>wat : Wat<number>
29+
30+
wat as Wat<string>;
31+
>wat as Wat<string> : Wat<string>
32+
>wat : Wat<number>
33+

0 commit comments

Comments
 (0)