Ensure instantiation expressions have symbols, preventing crash in signature relations (#56064)

This commit is contained in:
Jake Bailey 2023-11-07 15:59:19 -08:00 committed by GitHub
parent 649e614496
commit 4dd1e2f844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 283 additions and 15 deletions

View File

@ -6838,6 +6838,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const typeId = type.id;
const symbol = type.symbol;
if (symbol) {
const isInstantiationExpressionType = !!(getObjectFlags(type) & ObjectFlags.InstantiationExpressionType);
if (isInstantiationExpressionType) {
const instantiationExpressionType = type as InstantiationExpressionType;
const existing = instantiationExpressionType.node;
if (isTypeQueryNode(existing) && getTypeFromTypeNode(existing) === type) {
const typeNode = serializeExistingTypeNode(context, existing);
if (typeNode) {
return typeNode;
}
}
if (context.visitedTypes?.has(typeId)) {
return createElidedInformationPlaceholder(context);
}
return visitAndTransformType(type, createTypeNodeFromObjectType);
}
const isInstanceType = isClassInstanceSide(type) ? SymbolFlags.Type : SymbolFlags.Value;
if (isJSConstructor(symbol.valueDeclaration)) {
// 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 {
}
}
else {
const isInstantiationExpressionType = !!(getObjectFlags(type) & ObjectFlags.InstantiationExpressionType);
if (isInstantiationExpressionType) {
const instantiationExpressionType = type as InstantiationExpressionType;
if (isTypeQueryNode(instantiationExpressionType.node)) {
const typeNode = serializeExistingTypeNode(context, instantiationExpressionType.node);
if (typeNode) {
return typeNode;
}
}
if (context.visitedTypes?.has(typeId)) {
return createElidedInformationPlaceholder(context);
}
return visitAndTransformType(type, createTypeNodeFromObjectType);
}
// Anonymous types without a symbol are never circular.
return createTypeNodeFromObjectType(type);
}
@ -19542,6 +19543,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): AnonymousType {
Debug.assert(type.symbol, "anonymous type must have symbol to be instantiated");
const result = createObjectType(type.objectFlags & ~(ObjectFlags.CouldContainTypeVariablesComputed | ObjectFlags.CouldContainTypeVariables) | ObjectFlags.Instantiated, type.symbol) as AnonymousType;
if (type.objectFlags & ObjectFlags.Mapped) {
(result as MappedType).declaration = (type as MappedType).declaration;
@ -23074,6 +23076,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// method). Simply do a pairwise comparison of the signatures in the two signature lists instead
// of the much more expensive N * M comparison matrix we explore below. We erase type parameters
// as they are known to always be the same.
Debug.assertEqual(sourceSignatures.length, targetSignatures.length);
for (let i = 0; i < targetSignatures.length; i++) {
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, intersectionState, incompatibleReporter(sourceSignatures[i], targetSignatures[i]));
if (!related) {
@ -35677,7 +35680,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
hasSignatures ||= resolved.callSignatures.length !== 0 || resolved.constructSignatures.length !== 0;
hasApplicableSignature ||= callSignatures.length !== 0 || constructSignatures.length !== 0;
if (callSignatures !== resolved.callSignatures || constructSignatures !== resolved.constructSignatures) {
const result = createAnonymousType(/*symbol*/ undefined, resolved.members, callSignatures, constructSignatures, resolved.indexInfos) as ResolvedType & InstantiationExpressionType;
const result = createAnonymousType(createSymbol(SymbolFlags.None, InternalSymbolName.InstantiationExpression), resolved.members, callSignatures, constructSignatures, resolved.indexInfos) as ResolvedType & InstantiationExpressionType;
result.objectFlags |= ObjectFlags.InstantiationExpressionType;
result.node = node;
return result;

View File

@ -5955,6 +5955,7 @@ export const enum InternalSymbolName {
ExportEquals = "export=", // Export assignment symbol
Default = "default", // Default export symbol (technically not wholly internal, but included here for usability)
This = "this",
InstantiationExpression = "__instantiationExpression", // Instantiation expressions
}
/**

View File

@ -0,0 +1,23 @@
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.
Type '{ new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)' is not comparable to type '{ new (): ErrImpl<string>; prototype: ErrImpl<any>; }'.
Type 'ErrImpl<number>' is not comparable to type 'ErrImpl<string>'.
Type 'number' is not comparable to type 'string'.
==== aliasInstantiationExpressionGenericIntersectionNoCrash1.ts (1 errors) ====
class ErrImpl<E> {
e!: E;
}
declare const Err: typeof ErrImpl & (<T>() => T);
type ErrAlias<U> = typeof Err<U>;
declare const e: ErrAlias<number>;
e as ErrAlias<string>;
~~~~~~~~~~~~~~~~~~~~~
!!! 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.
!!! error TS2352: Type '{ new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)' is not comparable to type '{ new (): ErrImpl<string>; prototype: ErrImpl<any>; }'.
!!! error TS2352: Type 'ErrImpl<number>' is not comparable to type 'ErrImpl<string>'.
!!! error TS2352: Type 'number' is not comparable to type 'string'.

View File

@ -0,0 +1,23 @@
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash1.ts] ////
//// [aliasInstantiationExpressionGenericIntersectionNoCrash1.ts]
class ErrImpl<E> {
e!: E;
}
declare const Err: typeof ErrImpl & (<T>() => T);
type ErrAlias<U> = typeof Err<U>;
declare const e: ErrAlias<number>;
e as ErrAlias<string>;
//// [aliasInstantiationExpressionGenericIntersectionNoCrash1.js]
"use strict";
var ErrImpl = /** @class */ (function () {
function ErrImpl() {
}
return ErrImpl;
}());
e;

View File

@ -0,0 +1,32 @@
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash1.ts] ////
=== aliasInstantiationExpressionGenericIntersectionNoCrash1.ts ===
class ErrImpl<E> {
>ErrImpl : Symbol(ErrImpl, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 0))
>E : Symbol(E, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 14))
e!: E;
>e : Symbol(ErrImpl.e, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 18))
>E : Symbol(E, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 14))
}
declare const Err: typeof ErrImpl & (<T>() => T);
>Err : Symbol(Err, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 13))
>ErrImpl : Symbol(ErrImpl, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 0, 0))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 38))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 38))
type ErrAlias<U> = typeof Err<U>;
>ErrAlias : Symbol(ErrAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 49))
>U : Symbol(U, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 6, 14))
>Err : Symbol(Err, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 13))
>U : Symbol(U, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 6, 14))
declare const e: ErrAlias<number>;
>e : Symbol(e, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 8, 13))
>ErrAlias : Symbol(ErrAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 49))
e as ErrAlias<string>;
>e : Symbol(e, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 8, 13))
>ErrAlias : Symbol(ErrAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash1.ts, 4, 49))

View File

@ -0,0 +1,25 @@
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash1.ts] ////
=== aliasInstantiationExpressionGenericIntersectionNoCrash1.ts ===
class ErrImpl<E> {
>ErrImpl : ErrImpl<E>
e!: E;
>e : E
}
declare const Err: typeof ErrImpl & (<T>() => T);
>Err : typeof ErrImpl & (<T>() => T)
>ErrImpl : typeof ErrImpl
type ErrAlias<U> = typeof Err<U>;
>ErrAlias : { new (): ErrImpl<U>; prototype: ErrImpl<any>; } & (() => U)
>Err : typeof ErrImpl & (<T>() => T)
declare const e: ErrAlias<number>;
>e : { new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)
e as ErrAlias<string>;
>e as ErrAlias<string> : { new (): ErrImpl<string>; prototype: ErrImpl<any>; } & (() => string)
>e : { new (): ErrImpl<number>; prototype: ErrImpl<any>; } & (() => number)

View File

@ -0,0 +1,28 @@
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.
Type 'Wat<number>' is not comparable to type '{ new (): Class<string>; prototype: Class<any>; }'.
Type 'Class<number>' is not comparable to type 'Class<string>'.
Type 'number' is not comparable to type 'string'.
==== aliasInstantiationExpressionGenericIntersectionNoCrash2.ts (1 errors) ====
declare class Class<T> {
x: T;
}
declare function fn<T>(): T;
type ClassAlias<T> = typeof Class<T>;
type FnAlias<T> = typeof fn<T>;
type Wat<T> = ClassAlias<T> & FnAlias<T>;
declare const wat: Wat<number>;
wat as Wat<string>;
~~~~~~~~~~~~~~~~~~
!!! 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.
!!! error TS2352: Type 'Wat<number>' is not comparable to type '{ new (): Class<string>; prototype: Class<any>; }'.
!!! error TS2352: Type 'Class<number>' is not comparable to type 'Class<string>'.
!!! error TS2352: Type 'number' is not comparable to type 'string'.

View File

@ -0,0 +1,23 @@
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash2.ts] ////
//// [aliasInstantiationExpressionGenericIntersectionNoCrash2.ts]
declare class Class<T> {
x: T;
}
declare function fn<T>(): T;
type ClassAlias<T> = typeof Class<T>;
type FnAlias<T> = typeof fn<T>;
type Wat<T> = ClassAlias<T> & FnAlias<T>;
declare const wat: Wat<number>;
wat as Wat<string>;
//// [aliasInstantiationExpressionGenericIntersectionNoCrash2.js]
"use strict";
wat;

View File

@ -0,0 +1,47 @@
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash2.ts] ////
=== aliasInstantiationExpressionGenericIntersectionNoCrash2.ts ===
declare class Class<T> {
>Class : Symbol(Class, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 0))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 20))
x: T;
>x : Symbol(Class.x, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 24))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 20))
}
declare function fn<T>(): T;
>fn : Symbol(fn, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 2, 1))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 20))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 20))
type ClassAlias<T> = typeof Class<T>;
>ClassAlias : Symbol(ClassAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 28))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 16))
>Class : Symbol(Class, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 0, 0))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 16))
type FnAlias<T> = typeof fn<T>;
>FnAlias : Symbol(FnAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 37))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 13))
>fn : Symbol(fn, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 2, 1))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 13))
type Wat<T> = ClassAlias<T> & FnAlias<T>;
>Wat : Symbol(Wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 31))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 10, 9))
>ClassAlias : Symbol(ClassAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 4, 28))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 10, 9))
>FnAlias : Symbol(FnAlias, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 7, 37))
>T : Symbol(T, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 10, 9))
declare const wat: Wat<number>;
>wat : Symbol(wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 13, 13))
>Wat : Symbol(Wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 31))
wat as Wat<string>;
>wat : Symbol(wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 13, 13))
>Wat : Symbol(Wat, Decl(aliasInstantiationExpressionGenericIntersectionNoCrash2.ts, 8, 31))

View File

@ -0,0 +1,33 @@
//// [tests/cases/compiler/aliasInstantiationExpressionGenericIntersectionNoCrash2.ts] ////
=== aliasInstantiationExpressionGenericIntersectionNoCrash2.ts ===
declare class Class<T> {
>Class : Class<T>
x: T;
>x : T
}
declare function fn<T>(): T;
>fn : <T>() => T
type ClassAlias<T> = typeof Class<T>;
>ClassAlias : typeof Class<T>
>Class : typeof Class
type FnAlias<T> = typeof fn<T>;
>FnAlias : typeof fn<T>
>fn : <T_1>() => T_1
type Wat<T> = ClassAlias<T> & FnAlias<T>;
>Wat : Wat<T>
declare const wat: Wat<number>;
>wat : Wat<number>
wat as Wat<string>;
>wat as Wat<string> : Wat<string>
>wat : Wat<number>

View File

@ -7049,6 +7049,7 @@ declare namespace ts {
ExportEquals = "export=",
Default = "default",
This = "this",
InstantiationExpression = "__instantiationExpression",
}
/**
* This represents a string whose leading underscore have been escaped by adding extra leading underscores.

View File

@ -0,0 +1,12 @@
// @strict: true
class ErrImpl<E> {
e!: E;
}
declare const Err: typeof ErrImpl & (<T>() => T);
type ErrAlias<U> = typeof Err<U>;
declare const e: ErrAlias<number>;
e as ErrAlias<string>;

View File

@ -0,0 +1,17 @@
// @strict: true
declare class Class<T> {
x: T;
}
declare function fn<T>(): T;
type ClassAlias<T> = typeof Class<T>;
type FnAlias<T> = typeof fn<T>;
type Wat<T> = ClassAlias<T> & FnAlias<T>;
declare const wat: Wat<number>;
wat as Wat<string>;