Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14056,7 +14056,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (getObjectFlags(type) & ObjectFlags.Reference) {
const target = (type as TypeReference).target;
const typeArguments = getTypeArguments(type as TypeReference);
return length(target.typeParameters) === length(typeArguments) ? createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType!])) : type;
return length(target.typeParameters) === length(typeArguments) ? createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType!]), !!(getObjectFlags(type) & ObjectFlags.InstantiatedReference)) : type;
}
else if (type.flags & TypeFlags.Intersection) {
const types = sameMap((type as IntersectionType).types, t => getTypeWithThisArgument(t, thisArgument, needApparentType));
Expand Down Expand Up @@ -16919,13 +16919,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return createTypeReference(target, typeArguments);
}

function createTypeReference(target: GenericType, typeArguments: readonly Type[] | undefined): TypeReference {
const id = getTypeListId(typeArguments);
function createTypeReference(target: GenericType, typeArguments: readonly Type[] | undefined, instantiatedReference?: boolean): TypeReference {
const id = getTypeListId(typeArguments) + (instantiatedReference ? "*" : "");
let type = target.instantiations.get(id);
if (!type) {
type = createObjectType(ObjectFlags.Reference, target.symbol) as TypeReference;
target.instantiations.set(id, type);
type.objectFlags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments) : 0;
if (instantiatedReference) {
type.objectFlags |= ObjectFlags.InstantiatedReference;
}
type.target = target;
type.resolvedTypeArguments = typeArguments;
}
Expand Down Expand Up @@ -21064,7 +21067,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (objectFlags & ObjectFlags.Reference && !(type as TypeReference).node) {
const resolvedTypeArguments = (type as TypeReference).resolvedTypeArguments;
const newTypeArguments = instantiateTypes(resolvedTypeArguments, mapper);
return newTypeArguments !== resolvedTypeArguments ? createNormalizedTypeReference((type as TypeReference).target, newTypeArguments) : type;
return newTypeArguments === resolvedTypeArguments ? type :
(type as TypeReference).target.objectFlags & ObjectFlags.Tuple ? createNormalizedTupleType((type as TypeReference).target as TupleType, newTypeArguments!) :
createTypeReference((type as TypeReference).target, newTypeArguments, /*instantiatedReference*/ true);
}
if (objectFlags & ObjectFlags.ReverseMapped) {
return instantiateReverseMappedType(type as ReverseMappedType, mapper);
Expand Down Expand Up @@ -25085,7 +25090,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
const result = symbol.flags & SymbolFlags.TypeAlias ?
getTypeAliasInstantiation(symbol, instantiateTypes(getSymbolLinks(symbol).typeParameters!, mapper)) :
createTypeReference(type as GenericType, instantiateTypes((type as GenericType).typeParameters, mapper));
createTypeReference(type as GenericType, instantiateTypes((type as GenericType).typeParameters, mapper), /*instantiatedReference*/ true);
markerTypes.add(getTypeId(result));
return result;
}
Expand Down Expand Up @@ -25300,35 +25305,39 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getRecursionIdentity(type: Type): object {
// Object and array literals are known not to contain recursive references and don't need a recursion identity.
if (type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) {
if (getObjectFlags(type) & ObjectFlags.Reference && (type as TypeReference).node) {
const objectFlags = getObjectFlags(type);
if (objectFlags & ObjectFlags.Reference && (type as TypeReference).node) {
// Deferred type references are tracked through their associated AST node. This gives us finer
// granularity than using their associated target because each manifest type reference has a
// unique AST node.
return (type as TypeReference).node!;
}
if (type.symbol && !(getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol.flags & SymbolFlags.Class)) {
// We track object types that have a symbol by that symbol (representing the origin of the type), but
// exclude the static side of a class since it shares its symbol with the instance side.
if (
type.symbol && objectFlags & (ObjectFlags.Instantiated | ObjectFlags.InstantiatedReference) &&
!(objectFlags & ObjectFlags.Anonymous && type.symbol.flags & SymbolFlags.Class)
) {
// We track instantiated object types that have a symbol by that symbol (representing the origin of the
// type), but exclude the static side of a class since it shares its symbol with the instance side.
return type.symbol;
}
if (isTupleType(type)) {
return type.target;
}
}
if (type.flags & TypeFlags.TypeParameter) {
else if (type.flags & TypeFlags.TypeParameter) {
// We use the symbol of the type parameter such that all "fresh" instantiations of that type parameter
// have the same recursion identity.
return type.symbol;
}
if (type.flags & TypeFlags.IndexedAccess) {
else if (type.flags & TypeFlags.IndexedAccess) {
// Identity is the leftmost object type in a chain of indexed accesses, eg, in A[P1][P2][P3] it is A.
do {
type = (type as IndexedAccessType).objectType;
}
while (type.flags & TypeFlags.IndexedAccess);
return type;
}
if (type.flags & TypeFlags.Conditional) {
else if (type.flags & TypeFlags.Conditional) {
// The root object represents the origin of the conditional type
return (type as ConditionalType).root;
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6563,6 +6563,7 @@ export const enum ObjectFlags {
IdenticalBaseTypeCalculated = 1 << 25, // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already
/** @internal */
IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member
InstantiatedReference = 1 << 27, // Originates in instantiation of type reference
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ObjectFlags.InstantiatedReference is assigned 1 << 27, but ObjectFlags.SingleSignatureType already uses the same bit (1 << 27). This introduces a flag collision that can break ObjectTypeKindMask-based logic and makes it impossible to distinguish the two flags reliably. Please move InstantiatedReference to an unused bit (e.g. 1 << 28) and update all dependent baselines/usages accordingly.

Copilot uses AI. Check for mistakes.

// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
/** @internal */
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6719,6 +6719,7 @@ declare namespace ts {
ContainsSpread = 2097152,
ObjectRestType = 4194304,
InstantiationExpressionType = 8388608,
InstantiatedReference = 134217728,
}
interface ObjectType extends Type {
objectFlags: ObjectFlags;
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/arrayFrom.types
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ const inputALike: ArrayLike<A> = { length: 0 };
> : ^

const inputARand = getEither(inputA, inputALike);
>inputARand : ArrayLike<A> | Iterable<A>
>inputARand : Iterable<A> | ArrayLike<A>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^
>getEither(inputA, inputALike) : ArrayLike<A> | Iterable<A>
>getEither(inputA, inputALike) : Iterable<A> | ArrayLike<A>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => ArrayLike<T> | Iterable<T>
> : ^ ^^ ^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -203,7 +203,7 @@ const result8: A[] = Array.from(inputARand);
> : ^^^^^^^^^^^^^^^^
>from : { <T>(arrayLike: ArrayLike<T>): T[]; <T, U>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[]; <T>(iterable: Iterable<T> | ArrayLike<T>): T[]; <T, U>(iterable: Iterable<T> | ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[]; }
> : ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^^
>inputARand : ArrayLike<A> | Iterable<A>
>inputARand : Iterable<A> | ArrayLike<A>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^

const result9: B[] = Array.from(inputARand, ({ a }): B => ({ b: a }));
Expand All @@ -217,7 +217,7 @@ const result9: B[] = Array.from(inputARand, ({ a }): B => ({ b: a }));
> : ^^^^^^^^^^^^^^^^
>from : { <T>(arrayLike: ArrayLike<T>): T[]; <T, U>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[]; <T>(iterable: Iterable<T> | ArrayLike<T>): T[]; <T, U>(iterable: Iterable<T> | ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[]; }
> : ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^^
>inputARand : ArrayLike<A> | Iterable<A>
>inputARand : Iterable<A> | ArrayLike<A>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^
>({ a }): B => ({ b: a }) : ({ a }: A) => B
> : ^ ^^^^^^^^
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/arrayLiterals3.types
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ var temp2: [number[], string[]] = [[1, 2, 3], ["hello", "string"]];

interface tup {
0: number[]|string[];
>0 : number[] | string[]
>0 : string[] | number[]
> : ^^^^^^^^^^^^^^^^^^^

1: number[]|string[];
>1 : number[] | string[]
>1 : string[] | number[]
> : ^^^^^^^^^^^^^^^^^^^
}
interface myArray extends Array<Number> { }
Expand All @@ -111,7 +111,7 @@ var c0: tup = [...temp2]; // Error
> : ^^^
>[...temp2] : [number[], string[]]
> : ^^^^^^^^^^^^^^^^^^^^
>...temp2 : number[] | string[]
>...temp2 : string[] | number[]
> : ^^^^^^^^^^^^^^^^^^^
>temp2 : [number[], string[]]
> : ^^^^^^^^^^^^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//// [tests/cases/conformance/jsx/checkJsxChildrenCanBeTupleType.tsx] ////

=== Performance Stats ===
Assignability cache: 2,500
Assignability cache: 5,000
Type Count: 10,000
Instantiation count: 100,000
Symbol count: 100,000
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/checkJsxChildrenProperty16.types
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//// [tests/cases/conformance/jsx/checkJsxChildrenProperty16.tsx] ////

=== Performance Stats ===
Assignability cache: 2,500
Assignability cache: 5,000
Type Count: 10,000
Instantiation count: 100,000
Symbol count: 50,000
Symbol count: 100,000

=== checkJsxChildrenProperty16.tsx ===
/// <reference path="react16.d.ts" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//// [tests/cases/conformance/jsx/checkJsxUnionSFXContextualTypeInferredCorrectly.tsx] ////

=== Performance Stats ===
Assignability cache: 2,500
Assignability cache: 5,000
Type Count: 10,000
Instantiation count: 100,000
Symbol count: 50,000
Symbol count: 100,000

=== checkJsxUnionSFXContextualTypeInferredCorrectly.tsx ===
/// <reference path="react16.d.ts" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//// [tests/cases/compiler/circularlySimplifyingConditionalTypesNoCrash.ts] ////

=== Performance Stats ===
Instantiation count: 1,000

=== circularlySimplifyingConditionalTypesNoCrash.ts ===
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
>Omit : Omit<T, K>
Expand Down Expand Up @@ -68,8 +71,8 @@ declare var connect: Connect;
const myStoreConnect: Connect = function(
>myStoreConnect : Connect
> : ^^^^^^^
>function( mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options: unknown = {},) { return connect( mapStateToProps, mapDispatchToProps, mergeProps, options, );} : (mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options?: unknown) => InferableComponentEnhancerWithProps<TStateProps, Omit<P, Extract<keyof TStateProps, keyof P>> & TOwnProps>
> : ^ ^^^ ^^ ^^^ ^^ ^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>function( mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options: unknown = {},) { return connect( mapStateToProps, mapDispatchToProps, mergeProps, options, );} : (mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options?: unknown) => InferableComponentEnhancerWithProps<TStateProps, Omit<Shared<TStateProps, any>, Extract<keyof TStateProps, Extract<keyof TStateProps, string | number | symbol>>> & TOwnProps>
> : ^ ^^^ ^^ ^^^ ^^ ^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

mapStateToProps?: any,
>mapStateToProps : any
Expand All @@ -88,8 +91,8 @@ const myStoreConnect: Connect = function(

) {
return connect(
>connect( mapStateToProps, mapDispatchToProps, mergeProps, options, ) : InferableComponentEnhancerWithProps<TStateProps, Omit<P, Extract<keyof TStateProps, keyof P>> & TOwnProps>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>connect( mapStateToProps, mapDispatchToProps, mergeProps, options, ) : InferableComponentEnhancerWithProps<TStateProps, Omit<Shared<TStateProps, any>, Extract<keyof TStateProps, Extract<keyof TStateProps, string | number | symbol>>> & TOwnProps>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>connect : Connect
> : ^^^^^^^

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ immutable.ts(289,22): error TS2320: Interface 'Indexed<T>' cannot simultaneously
immutable.ts(305,22): error TS2320: Interface 'Set<T>' cannot simultaneously extend types 'Seq<never, T>' and 'Set<T>'.
Named property 'size' of types 'Seq<never, T>' and 'Set<T>' are not identical.
immutable.ts(323,20): error TS2430: Interface 'Seq<K, V>' incorrectly extends interface 'Collection<K, V>'.
The types of 'map(...).size' are incompatible between these types.
The types of 'map(...).filter(...).size' are incompatible between these types.
Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
immutable.ts(341,22): error TS2430: Interface 'Keyed<K, V>' incorrectly extends interface 'Collection<K, V>'.
Expand Down Expand Up @@ -382,7 +382,7 @@ immutable.ts(391,22): error TS2430: Interface 'Set<T>' incorrectly extends inter
export interface Seq<K, V> extends Collection<K, V> {
~~~
!!! error TS2430: Interface 'Seq<K, V>' incorrectly extends interface 'Collection<K, V>'.
!!! error TS2430: The types of 'map(...).size' are incompatible between these types.
!!! error TS2430: The types of 'map(...).filter(...).size' are incompatible between these types.
!!! error TS2430: Type 'number | undefined' is not assignable to type 'number'.
!!! error TS2430: Type 'undefined' is not assignable to type 'number'.
readonly size: number | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Identity cache: 2,500
Assignability cache: 5,000
Type Count: 50,000
Instantiation count: 100,000
Instantiation count: 250,000
Symbol count: 100,000

=== complex.ts ===
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/contextuallyTypedJsxChildren2.types
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//// [tests/cases/compiler/contextuallyTypedJsxChildren2.tsx] ////

=== Performance Stats ===
Assignability cache: 2,500
Assignability cache: 5,000
Type Count: 10,000
Instantiation count: 100,000
Symbol count: 50,000
Symbol count: 100,000

=== contextuallyTypedJsxChildren2.tsx ===
/// <reference path="react16.d.ts" />
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/controlFlowOptionalChain3.types
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Assignability cache: 2,500
Type Count: 10,000
Instantiation count: 100,000
Symbol count: 50,000
Symbol count: 100,000

=== controlFlowOptionalChain3.tsx ===
/// <reference path="react16.d.ts" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//// [tests/cases/compiler/discriminateWithOptionalProperty2.ts] ////

=== Performance Stats ===
Type Count: 1,000

=== discriminateWithOptionalProperty2.ts ===
// https://github.com/microsoft/TypeScript/issues/55532#issuecomment-1694744665

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//// [tests/cases/compiler/discriminateWithOptionalProperty2.ts] ////

=== Performance Stats ===
Type Count: 1,000

=== discriminateWithOptionalProperty2.ts ===
// https://github.com/microsoft/TypeScript/issues/55532#issuecomment-1694744665

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//// [tests/cases/compiler/errorInfoForRelatedIndexTypesNoConstraintElaboration.ts] ////

=== Performance Stats ===
Assignability cache: 2,500
Assignability cache: 5,000
Type Count: 10,000
Instantiation count: 250,000
Symbol count: 100,000
Expand Down
Loading
Loading