diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f2eaea833df0..b8d7146efb706 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1538,8 +1538,8 @@ namespace ts { function createType(flags: TypeFlags): Type { const result = new Type(checker, flags); - result.id = typeCount; typeCount++; + result.id = typeCount; return result; } @@ -4000,6 +4000,9 @@ namespace ts { return createTypeReference((type).target, concatenate((type).typeArguments, [thisArgument || (type).target.thisType])); } + if (type.flags & TypeFlags.Tuple) { + return createTupleType((type as TupleType).elementTypes, thisArgument); + } return type; } @@ -4103,7 +4106,8 @@ namespace ts { function resolveTupleTypeMembers(type: TupleType) { const arrayElementType = getUnionType(type.elementTypes); // Make the tuple type itself the 'this' type by including an extra type argument - const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type])); + // (Unless it's provided in the case that the tuple is a type parameter constraint) + const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type.thisType || type])); const members = createTupleTypeMemberSymbols(type.elementTypes); addInheritedMembers(members, arrayType.properties); setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo); @@ -5230,15 +5234,16 @@ namespace ts { return links.resolvedType; } - function createTupleType(elementTypes: Type[]) { - const id = getTypeListId(elementTypes); - return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes)); + function createTupleType(elementTypes: Type[], thisType?: Type) { + const id = getTypeListId(elementTypes) + "," + (thisType ? thisType.id : 0); + return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes, thisType)); } - function createNewTupleType(elementTypes: Type[]) { + function createNewTupleType(elementTypes: Type[], thisType?: Type) { const propagatedFlags = getPropagatingFlagsOfTypes(elementTypes, /*excludeKinds*/ 0); const type = createObjectType(TypeFlags.Tuple | propagatedFlags); type.elementTypes = elementTypes; + type.thisType = thisType; return type; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a6e860450c669..25630059bf094 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2371,6 +2371,7 @@ namespace ts { export interface TupleType extends ObjectType { elementTypes: Type[]; // Element types + thisType?: Type; // This-type of tuple (only needed for tuples that are constraints of type parameters) } export interface UnionOrIntersectionType extends Type { diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.js b/tests/baselines/reference/thisInTupleTypeParameterConstraints.js new file mode 100644 index 0000000000000..c63fbf6199124 --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.js @@ -0,0 +1,29 @@ +//// [thisInTupleTypeParameterConstraints.ts] +/// + +interface Boolean {} +interface IArguments {} +interface Function {} +interface Number {} +interface RegExp {} +interface Object {} +interface String {} + +interface Array { + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; + reduceRight(arg: this): void; + reduce(arg: this): void; + reduce2(arg: this): void; +} + +declare function f number]>(a: T): void; +let x: [(x: number) => number]; +f(x); + + +//// [thisInTupleTypeParameterConstraints.js] +/// +var x; +f(x); diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols b/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols new file mode 100644 index 0000000000000..4d2bef22c55fe --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols @@ -0,0 +1,66 @@ +=== tests/cases/compiler/thisInTupleTypeParameterConstraints.ts === +/// + +interface Boolean {} +>Boolean : Symbol(Boolean, Decl(thisInTupleTypeParameterConstraints.ts, 0, 0)) + +interface IArguments {} +>IArguments : Symbol(IArguments, Decl(thisInTupleTypeParameterConstraints.ts, 2, 20)) + +interface Function {} +>Function : Symbol(Function, Decl(thisInTupleTypeParameterConstraints.ts, 3, 23)) + +interface Number {} +>Number : Symbol(Number, Decl(thisInTupleTypeParameterConstraints.ts, 4, 21)) + +interface RegExp {} +>RegExp : Symbol(RegExp, Decl(thisInTupleTypeParameterConstraints.ts, 5, 19)) + +interface Object {} +>Object : Symbol(Object, Decl(thisInTupleTypeParameterConstraints.ts, 6, 19)) + +interface String {} +>String : Symbol(String, Decl(thisInTupleTypeParameterConstraints.ts, 7, 19)) + +interface Array { +>Array : Symbol(Array, Decl(thisInTupleTypeParameterConstraints.ts, 8, 19)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 10, 16)) + + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; +>map : Symbol(Array.map, Decl(thisInTupleTypeParameterConstraints.ts, 10, 20)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 13, 8)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 13, 11)) + + reduceRight(arg: this): void; +>reduceRight : Symbol(Array.reduceRight, Decl(thisInTupleTypeParameterConstraints.ts, 13, 28)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 14, 16)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 14, 19)) + + reduce(arg: this): void; +>reduce : Symbol(Array.reduce, Decl(thisInTupleTypeParameterConstraints.ts, 14, 36)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 15, 11)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 15, 14)) + + reduce2(arg: this): void; +>reduce2 : Symbol(Array.reduce2, Decl(thisInTupleTypeParameterConstraints.ts, 15, 31)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 16, 12)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 16, 15)) +} + +declare function f number]>(a: T): void; +>f : Symbol(f, Decl(thisInTupleTypeParameterConstraints.ts, 17, 1)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 19, 19)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 19, 31)) +>a : Symbol(a, Decl(thisInTupleTypeParameterConstraints.ts, 19, 54)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 19, 19)) + +let x: [(x: number) => number]; +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 3)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 9)) + +f(x); +>f : Symbol(f, Decl(thisInTupleTypeParameterConstraints.ts, 17, 1)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 3)) + diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.types b/tests/baselines/reference/thisInTupleTypeParameterConstraints.types new file mode 100644 index 0000000000000..7daafe02bfc7e --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/thisInTupleTypeParameterConstraints.ts === +/// + +interface Boolean {} +>Boolean : Boolean + +interface IArguments {} +>IArguments : IArguments + +interface Function {} +>Function : Function + +interface Number {} +>Number : Number + +interface RegExp {} +>RegExp : RegExp + +interface Object {} +>Object : Object + +interface String {} +>String : String + +interface Array { +>Array : T[] +>T : T + + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; +>map : (arg: this) => void +>U : U +>arg : this + + reduceRight(arg: this): void; +>reduceRight : (arg: this) => void +>U : U +>arg : this + + reduce(arg: this): void; +>reduce : (arg: this) => void +>U : U +>arg : this + + reduce2(arg: this): void; +>reduce2 : (arg: this) => void +>U : U +>arg : this +} + +declare function f number]>(a: T): void; +>f : number]>(a: T) => void +>T : T +>x : number +>a : T +>T : T + +let x: [(x: number) => number]; +>x : [(x: number) => number] +>x : number + +f(x); +>f(x) : void +>f : number]>(a: T) => void +>x : [(x: number) => number] + diff --git a/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts b/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts new file mode 100644 index 0000000000000..b6d0d338d85c5 --- /dev/null +++ b/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts @@ -0,0 +1,22 @@ +/// + +interface Boolean {} +interface IArguments {} +interface Function {} +interface Number {} +interface RegExp {} +interface Object {} +interface String {} + +interface Array { + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; + reduceRight(arg: this): void; + reduce(arg: this): void; + reduce2(arg: this): void; +} + +declare function f number]>(a: T): void; +let x: [(x: number) => number]; +f(x);