Skip to content

Commit 73fbe5a

Browse files
authored
Merge pull request #13356 from Microsoft/fixCombinedTypeMappers
Fix nested generic mapped type instantiations
2 parents e4dcd08 + 70763da commit 73fbe5a

5 files changed

+153
-16
lines changed

src/compiler/checker.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4606,7 +4606,7 @@ namespace ts {
46064606
// Create a mapper from T to the current iteration type constituent. Then, if the
46074607
// mapped type is itself an instantiated type, combine the iteration mapper with the
46084608
// instantiation mapper.
4609-
const iterationMapper = createUnaryTypeMapper(typeParameter, t);
4609+
const iterationMapper = createTypeMapper([typeParameter], [t]);
46104610
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
46114611
const propType = instantiateType(templateType, templateMapper);
46124612
// If the current iteration type constituent is a string literal type, create a property.
@@ -4666,7 +4666,7 @@ namespace ts {
46664666
}
46674667

46684668
function getErasedTemplateTypeFromMappedType(type: MappedType) {
4669-
return instantiateType(getTemplateTypeFromMappedType(type), createUnaryTypeMapper(getTypeParameterFromMappedType(type), anyType));
4669+
return instantiateType(getTemplateTypeFromMappedType(type), createTypeEraser([getTypeParameterFromMappedType(type)]));
46704670
}
46714671

46724672
function isGenericMappedType(type: Type) {
@@ -6135,7 +6135,7 @@ namespace ts {
61356135
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
61366136
return unknownType;
61376137
}
6138-
const mapper = createUnaryTypeMapper(getTypeParameterFromMappedType(type), indexType);
6138+
const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]);
61396139
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
61406140
return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
61416141
}
@@ -6506,16 +6506,16 @@ namespace ts {
65066506
return <T>instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper));
65076507
}
65086508

6509-
function createUnaryTypeMapper(source: Type, target: Type): TypeMapper {
6510-
return t => t === source ? target : t;
6509+
function makeUnaryTypeMapper(source: Type, target: Type) {
6510+
return (t: Type) => t === source ? target : t;
65116511
}
65126512

6513-
function createBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type): TypeMapper {
6514-
return t => t === source1 ? target1 : t === source2 ? target2 : t;
6513+
function makeBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type) {
6514+
return (t: Type) => t === source1 ? target1 : t === source2 ? target2 : t;
65156515
}
65166516

6517-
function createArrayTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
6518-
return t => {
6517+
function makeArrayTypeMapper(sources: Type[], targets: Type[]) {
6518+
return (t: Type) => {
65196519
for (let i = 0; i < sources.length; i++) {
65206520
if (t === sources[i]) {
65216521
return targets ? targets[i] : anyType;
@@ -6526,11 +6526,9 @@ namespace ts {
65266526
}
65276527

65286528
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
6529-
const count = sources.length;
6530-
const mapper: TypeMapper =
6531-
count == 1 ? createUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
6532-
count == 2 ? createBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
6533-
createArrayTypeMapper(sources, targets);
6529+
const mapper: TypeMapper = sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
6530+
sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
6531+
makeArrayTypeMapper(sources, targets);
65346532
mapper.mappedTypes = sources;
65356533
return mapper;
65366534
}
@@ -6564,7 +6562,7 @@ namespace ts {
65646562

65656563
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
65666564
const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
6567-
mapper.mappedTypes = mapper1.mappedTypes;
6565+
mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes);
65686566
return mapper;
65696567
}
65706568

@@ -6666,7 +6664,7 @@ namespace ts {
66666664
if (typeVariable !== mappedTypeVariable) {
66676665
return mapType(mappedTypeVariable, t => {
66686666
if (isMappableType(t)) {
6669-
const replacementMapper = createUnaryTypeMapper(typeVariable, t);
6667+
const replacementMapper = createTypeMapper([typeVariable], [t]);
66706668
const combinedMapper = mapper.mappedTypes && mapper.mappedTypes.length === 1 ? replacementMapper : combineTypeMappers(replacementMapper, mapper);
66716669
combinedMapper.mappedTypes = mapper.mappedTypes;
66726670
return instantiateMappedObjectType(type, combinedMapper);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [mappedTypeNestedGenericInstantiation.ts]
2+
// Repro from #13346
3+
4+
interface Chainable<T> {
5+
value(): T;
6+
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
7+
}
8+
9+
declare function chain<T>(t: T): Chainable<T>;
10+
11+
const square = (x: number) => x * x;
12+
13+
const v = chain({a: 1, b: 2}).mapValues(square).value();
14+
15+
16+
//// [mappedTypeNestedGenericInstantiation.js]
17+
// Repro from #13346
18+
var square = function (x) { return x * x; };
19+
var v = chain({ a: 1, b: 2 }).mapValues(square).value();
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
=== tests/cases/compiler/mappedTypeNestedGenericInstantiation.ts ===
2+
// Repro from #13346
3+
4+
interface Chainable<T> {
5+
>Chainable : Symbol(Chainable, Decl(mappedTypeNestedGenericInstantiation.ts, 0, 0))
6+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
7+
8+
value(): T;
9+
>value : Symbol(Chainable.value, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 24))
10+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
11+
12+
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
13+
>mapValues : Symbol(Chainable.mapValues, Decl(mappedTypeNestedGenericInstantiation.ts, 3, 13))
14+
>U : Symbol(U, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 12))
15+
>func : Symbol(func, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 15))
16+
>v : Symbol(v, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 22))
17+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
18+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
19+
>U : Symbol(U, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 12))
20+
>Chainable : Symbol(Chainable, Decl(mappedTypeNestedGenericInstantiation.ts, 0, 0))
21+
>k : Symbol(k, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 56))
22+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
23+
>U : Symbol(U, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 12))
24+
}
25+
26+
declare function chain<T>(t: T): Chainable<T>;
27+
>chain : Symbol(chain, Decl(mappedTypeNestedGenericInstantiation.ts, 5, 1))
28+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 23))
29+
>t : Symbol(t, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 26))
30+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 23))
31+
>Chainable : Symbol(Chainable, Decl(mappedTypeNestedGenericInstantiation.ts, 0, 0))
32+
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 23))
33+
34+
const square = (x: number) => x * x;
35+
>square : Symbol(square, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 5))
36+
>x : Symbol(x, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 16))
37+
>x : Symbol(x, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 16))
38+
>x : Symbol(x, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 16))
39+
40+
const v = chain({a: 1, b: 2}).mapValues(square).value();
41+
>v : Symbol(v, Decl(mappedTypeNestedGenericInstantiation.ts, 11, 5))
42+
>chain({a: 1, b: 2}).mapValues(square).value : Symbol(Chainable.value, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 24))
43+
>chain({a: 1, b: 2}).mapValues : Symbol(Chainable.mapValues, Decl(mappedTypeNestedGenericInstantiation.ts, 3, 13))
44+
>chain : Symbol(chain, Decl(mappedTypeNestedGenericInstantiation.ts, 5, 1))
45+
>a : Symbol(a, Decl(mappedTypeNestedGenericInstantiation.ts, 11, 17))
46+
>b : Symbol(b, Decl(mappedTypeNestedGenericInstantiation.ts, 11, 22))
47+
>mapValues : Symbol(Chainable.mapValues, Decl(mappedTypeNestedGenericInstantiation.ts, 3, 13))
48+
>square : Symbol(square, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 5))
49+
>value : Symbol(Chainable.value, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 24))
50+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
=== tests/cases/compiler/mappedTypeNestedGenericInstantiation.ts ===
2+
// Repro from #13346
3+
4+
interface Chainable<T> {
5+
>Chainable : Chainable<T>
6+
>T : T
7+
8+
value(): T;
9+
>value : () => T
10+
>T : T
11+
12+
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
13+
>mapValues : <U>(func: (v: T[keyof T]) => U) => Chainable<{ [k in keyof T]: U; }>
14+
>U : U
15+
>func : (v: T[keyof T]) => U
16+
>v : T[keyof T]
17+
>T : T
18+
>T : T
19+
>U : U
20+
>Chainable : Chainable<T>
21+
>k : k
22+
>T : T
23+
>U : U
24+
}
25+
26+
declare function chain<T>(t: T): Chainable<T>;
27+
>chain : <T>(t: T) => Chainable<T>
28+
>T : T
29+
>t : T
30+
>T : T
31+
>Chainable : Chainable<T>
32+
>T : T
33+
34+
const square = (x: number) => x * x;
35+
>square : (x: number) => number
36+
>(x: number) => x * x : (x: number) => number
37+
>x : number
38+
>x * x : number
39+
>x : number
40+
>x : number
41+
42+
const v = chain({a: 1, b: 2}).mapValues(square).value();
43+
>v : { a: number; b: number; }
44+
>chain({a: 1, b: 2}).mapValues(square).value() : { a: number; b: number; }
45+
>chain({a: 1, b: 2}).mapValues(square).value : () => { a: number; b: number; }
46+
>chain({a: 1, b: 2}).mapValues(square) : Chainable<{ a: number; b: number; }>
47+
>chain({a: 1, b: 2}).mapValues : <U>(func: (v: number) => U) => Chainable<{ a: U; b: U; }>
48+
>chain({a: 1, b: 2}) : Chainable<{ a: number; b: number; }>
49+
>chain : <T>(t: T) => Chainable<T>
50+
>{a: 1, b: 2} : { a: number; b: number; }
51+
>a : number
52+
>1 : 1
53+
>b : number
54+
>2 : 2
55+
>mapValues : <U>(func: (v: number) => U) => Chainable<{ a: U; b: U; }>
56+
>square : (x: number) => number
57+
>value : () => { a: number; b: number; }
58+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Repro from #13346
2+
3+
interface Chainable<T> {
4+
value(): T;
5+
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
6+
}
7+
8+
declare function chain<T>(t: T): Chainable<T>;
9+
10+
const square = (x: number) => x * x;
11+
12+
const v = chain({a: 1, b: 2}).mapValues(square).value();

0 commit comments

Comments
 (0)