Merge pull request #13356 from Microsoft/fixCombinedTypeMappers

Fix nested generic mapped type instantiations
This commit is contained in:
Anders Hejlsberg 2017-01-09 09:30:19 -10:00 committed by GitHub
commit 73fbe5a8e7
5 changed files with 153 additions and 16 deletions

View file

@ -4606,7 +4606,7 @@ namespace ts {
// Create a mapper from T to the current iteration type constituent. Then, if the
// mapped type is itself an instantiated type, combine the iteration mapper with the
// instantiation mapper.
const iterationMapper = createUnaryTypeMapper(typeParameter, t);
const iterationMapper = createTypeMapper([typeParameter], [t]);
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
const propType = instantiateType(templateType, templateMapper);
// If the current iteration type constituent is a string literal type, create a property.
@ -4666,7 +4666,7 @@ namespace ts {
}
function getErasedTemplateTypeFromMappedType(type: MappedType) {
return instantiateType(getTemplateTypeFromMappedType(type), createUnaryTypeMapper(getTypeParameterFromMappedType(type), anyType));
return instantiateType(getTemplateTypeFromMappedType(type), createTypeEraser([getTypeParameterFromMappedType(type)]));
}
function isGenericMappedType(type: Type) {
@ -6135,7 +6135,7 @@ namespace ts {
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
return unknownType;
}
const mapper = createUnaryTypeMapper(getTypeParameterFromMappedType(type), indexType);
const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]);
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
}
@ -6506,16 +6506,16 @@ namespace ts {
return <T>instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper));
}
function createUnaryTypeMapper(source: Type, target: Type): TypeMapper {
return t => t === source ? target : t;
function makeUnaryTypeMapper(source: Type, target: Type) {
return (t: Type) => t === source ? target : t;
}
function createBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type): TypeMapper {
return t => t === source1 ? target1 : t === source2 ? target2 : t;
function makeBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type) {
return (t: Type) => t === source1 ? target1 : t === source2 ? target2 : t;
}
function createArrayTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
return t => {
function makeArrayTypeMapper(sources: Type[], targets: Type[]) {
return (t: Type) => {
for (let i = 0; i < sources.length; i++) {
if (t === sources[i]) {
return targets ? targets[i] : anyType;
@ -6526,11 +6526,9 @@ namespace ts {
}
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
const count = sources.length;
const mapper: TypeMapper =
count == 1 ? createUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
count == 2 ? createBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
createArrayTypeMapper(sources, targets);
const mapper: TypeMapper = sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
makeArrayTypeMapper(sources, targets);
mapper.mappedTypes = sources;
return mapper;
}
@ -6564,7 +6562,7 @@ namespace ts {
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
mapper.mappedTypes = mapper1.mappedTypes;
mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes);
return mapper;
}
@ -6666,7 +6664,7 @@ namespace ts {
if (typeVariable !== mappedTypeVariable) {
return mapType(mappedTypeVariable, t => {
if (isMappableType(t)) {
const replacementMapper = createUnaryTypeMapper(typeVariable, t);
const replacementMapper = createTypeMapper([typeVariable], [t]);
const combinedMapper = mapper.mappedTypes && mapper.mappedTypes.length === 1 ? replacementMapper : combineTypeMappers(replacementMapper, mapper);
combinedMapper.mappedTypes = mapper.mappedTypes;
return instantiateMappedObjectType(type, combinedMapper);

View file

@ -0,0 +1,19 @@
//// [mappedTypeNestedGenericInstantiation.ts]
// Repro from #13346
interface Chainable<T> {
value(): T;
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
}
declare function chain<T>(t: T): Chainable<T>;
const square = (x: number) => x * x;
const v = chain({a: 1, b: 2}).mapValues(square).value();
//// [mappedTypeNestedGenericInstantiation.js]
// Repro from #13346
var square = function (x) { return x * x; };
var v = chain({ a: 1, b: 2 }).mapValues(square).value();

View file

@ -0,0 +1,50 @@
=== tests/cases/compiler/mappedTypeNestedGenericInstantiation.ts ===
// Repro from #13346
interface Chainable<T> {
>Chainable : Symbol(Chainable, Decl(mappedTypeNestedGenericInstantiation.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
value(): T;
>value : Symbol(Chainable.value, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 24))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
>mapValues : Symbol(Chainable.mapValues, Decl(mappedTypeNestedGenericInstantiation.ts, 3, 13))
>U : Symbol(U, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 12))
>func : Symbol(func, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 15))
>v : Symbol(v, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 22))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
>U : Symbol(U, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 12))
>Chainable : Symbol(Chainable, Decl(mappedTypeNestedGenericInstantiation.ts, 0, 0))
>k : Symbol(k, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 56))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 20))
>U : Symbol(U, Decl(mappedTypeNestedGenericInstantiation.ts, 4, 12))
}
declare function chain<T>(t: T): Chainable<T>;
>chain : Symbol(chain, Decl(mappedTypeNestedGenericInstantiation.ts, 5, 1))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 23))
>t : Symbol(t, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 26))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 23))
>Chainable : Symbol(Chainable, Decl(mappedTypeNestedGenericInstantiation.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeNestedGenericInstantiation.ts, 7, 23))
const square = (x: number) => x * x;
>square : Symbol(square, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 5))
>x : Symbol(x, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 16))
>x : Symbol(x, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 16))
>x : Symbol(x, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 16))
const v = chain({a: 1, b: 2}).mapValues(square).value();
>v : Symbol(v, Decl(mappedTypeNestedGenericInstantiation.ts, 11, 5))
>chain({a: 1, b: 2}).mapValues(square).value : Symbol(Chainable.value, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 24))
>chain({a: 1, b: 2}).mapValues : Symbol(Chainable.mapValues, Decl(mappedTypeNestedGenericInstantiation.ts, 3, 13))
>chain : Symbol(chain, Decl(mappedTypeNestedGenericInstantiation.ts, 5, 1))
>a : Symbol(a, Decl(mappedTypeNestedGenericInstantiation.ts, 11, 17))
>b : Symbol(b, Decl(mappedTypeNestedGenericInstantiation.ts, 11, 22))
>mapValues : Symbol(Chainable.mapValues, Decl(mappedTypeNestedGenericInstantiation.ts, 3, 13))
>square : Symbol(square, Decl(mappedTypeNestedGenericInstantiation.ts, 9, 5))
>value : Symbol(Chainable.value, Decl(mappedTypeNestedGenericInstantiation.ts, 2, 24))

View file

@ -0,0 +1,58 @@
=== tests/cases/compiler/mappedTypeNestedGenericInstantiation.ts ===
// Repro from #13346
interface Chainable<T> {
>Chainable : Chainable<T>
>T : T
value(): T;
>value : () => T
>T : T
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
>mapValues : <U>(func: (v: T[keyof T]) => U) => Chainable<{ [k in keyof T]: U; }>
>U : U
>func : (v: T[keyof T]) => U
>v : T[keyof T]
>T : T
>T : T
>U : U
>Chainable : Chainable<T>
>k : k
>T : T
>U : U
}
declare function chain<T>(t: T): Chainable<T>;
>chain : <T>(t: T) => Chainable<T>
>T : T
>t : T
>T : T
>Chainable : Chainable<T>
>T : T
const square = (x: number) => x * x;
>square : (x: number) => number
>(x: number) => x * x : (x: number) => number
>x : number
>x * x : number
>x : number
>x : number
const v = chain({a: 1, b: 2}).mapValues(square).value();
>v : { a: number; b: number; }
>chain({a: 1, b: 2}).mapValues(square).value() : { a: number; b: number; }
>chain({a: 1, b: 2}).mapValues(square).value : () => { a: number; b: number; }
>chain({a: 1, b: 2}).mapValues(square) : Chainable<{ a: number; b: number; }>
>chain({a: 1, b: 2}).mapValues : <U>(func: (v: number) => U) => Chainable<{ a: U; b: U; }>
>chain({a: 1, b: 2}) : Chainable<{ a: number; b: number; }>
>chain : <T>(t: T) => Chainable<T>
>{a: 1, b: 2} : { a: number; b: number; }
>a : number
>1 : 1
>b : number
>2 : 2
>mapValues : <U>(func: (v: number) => U) => Chainable<{ a: U; b: U; }>
>square : (x: number) => number
>value : () => { a: number; b: number; }

View file

@ -0,0 +1,12 @@
// Repro from #13346
interface Chainable<T> {
value(): T;
mapValues<U>(func: (v: T[keyof T]) => U): Chainable<{[k in keyof T]: U}>;
}
declare function chain<T>(t: T): Chainable<T>;
const square = (x: number) => x * x;
const v = chain({a: 1, b: 2}).mapValues(square).value();