From d19fb98ec6e1cfe3f6521bd1cc1dbfcb4e6e82a7 Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Thu, 11 Oct 2018 18:45:51 -0400 Subject: [PATCH] When instantiating a mapped type, clone the type parameter. (#27597) This gives the type parameter returned by getTypeParameterFromMappedType an accurate constraint. Fixes #27596. --- src/compiler/checker.ts | 8 ++++++- .../mappedTypeParameterConstraint.js | 14 +++++++++++ .../mappedTypeParameterConstraint.symbols | 23 +++++++++++++++++++ .../mappedTypeParameterConstraint.types | 14 +++++++++++ .../compiler/mappedTypeParameterConstraint.ts | 6 +++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/mappedTypeParameterConstraint.js create mode 100644 tests/baselines/reference/mappedTypeParameterConstraint.symbols create mode 100644 tests/baselines/reference/mappedTypeParameterConstraint.types create mode 100644 tests/cases/compiler/mappedTypeParameterConstraint.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc93cce86b89f..77d27de786be1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6850,7 +6850,7 @@ namespace ts { function getConstraintTypeFromMappedType(type: MappedType) { return type.constraintType || - (type.constraintType = instantiateType(getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)), type.mapper || identityMapper) || errorType); + (type.constraintType = getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)) || errorType); } function getTemplateTypeFromMappedType(type: MappedType) { @@ -10413,6 +10413,12 @@ namespace ts { const result = createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol); if (type.objectFlags & ObjectFlags.Mapped) { (result).declaration = (type).declaration; + // C.f. instantiateSignature + const origTypeParameter = getTypeParameterFromMappedType(type); + const freshTypeParameter = cloneTypeParameter(origTypeParameter); + (result).typeParameter = freshTypeParameter; + mapper = combineTypeMappers(makeUnaryTypeMapper(origTypeParameter, freshTypeParameter), mapper); + freshTypeParameter.mapper = mapper; } result.target = type; result.mapper = mapper; diff --git a/tests/baselines/reference/mappedTypeParameterConstraint.js b/tests/baselines/reference/mappedTypeParameterConstraint.js new file mode 100644 index 0000000000000..f7715f1799338 --- /dev/null +++ b/tests/baselines/reference/mappedTypeParameterConstraint.js @@ -0,0 +1,14 @@ +//// [mappedTypeParameterConstraint.ts] +// Repro for #27596 + +type MyMap = {[P in keyof T]: T[keyof T]}; +function foo(arg: U): MyMap { + return arg; +} + + +//// [mappedTypeParameterConstraint.js] +// Repro for #27596 +function foo(arg) { + return arg; +} diff --git a/tests/baselines/reference/mappedTypeParameterConstraint.symbols b/tests/baselines/reference/mappedTypeParameterConstraint.symbols new file mode 100644 index 0000000000000..1609c46500525 --- /dev/null +++ b/tests/baselines/reference/mappedTypeParameterConstraint.symbols @@ -0,0 +1,23 @@ +=== tests/cases/compiler/mappedTypeParameterConstraint.ts === +// Repro for #27596 + +type MyMap = {[P in keyof T]: T[keyof T]}; +>MyMap : Symbol(MyMap, Decl(mappedTypeParameterConstraint.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypeParameterConstraint.ts, 2, 11)) +>P : Symbol(P, Decl(mappedTypeParameterConstraint.ts, 2, 18)) +>T : Symbol(T, Decl(mappedTypeParameterConstraint.ts, 2, 11)) +>T : Symbol(T, Decl(mappedTypeParameterConstraint.ts, 2, 11)) +>T : Symbol(T, Decl(mappedTypeParameterConstraint.ts, 2, 11)) + +function foo(arg: U): MyMap { +>foo : Symbol(foo, Decl(mappedTypeParameterConstraint.ts, 2, 45)) +>U : Symbol(U, Decl(mappedTypeParameterConstraint.ts, 3, 13)) +>arg : Symbol(arg, Decl(mappedTypeParameterConstraint.ts, 3, 16)) +>U : Symbol(U, Decl(mappedTypeParameterConstraint.ts, 3, 13)) +>MyMap : Symbol(MyMap, Decl(mappedTypeParameterConstraint.ts, 0, 0)) +>U : Symbol(U, Decl(mappedTypeParameterConstraint.ts, 3, 13)) + + return arg; +>arg : Symbol(arg, Decl(mappedTypeParameterConstraint.ts, 3, 16)) +} + diff --git a/tests/baselines/reference/mappedTypeParameterConstraint.types b/tests/baselines/reference/mappedTypeParameterConstraint.types new file mode 100644 index 0000000000000..54af46ecfcf20 --- /dev/null +++ b/tests/baselines/reference/mappedTypeParameterConstraint.types @@ -0,0 +1,14 @@ +=== tests/cases/compiler/mappedTypeParameterConstraint.ts === +// Repro for #27596 + +type MyMap = {[P in keyof T]: T[keyof T]}; +>MyMap : MyMap + +function foo(arg: U): MyMap { +>foo : (arg: U) => MyMap +>arg : U + + return arg; +>arg : U +} + diff --git a/tests/cases/compiler/mappedTypeParameterConstraint.ts b/tests/cases/compiler/mappedTypeParameterConstraint.ts new file mode 100644 index 0000000000000..16b37999c92b2 --- /dev/null +++ b/tests/cases/compiler/mappedTypeParameterConstraint.ts @@ -0,0 +1,6 @@ +// Repro for #27596 + +type MyMap = {[P in keyof T]: T[keyof T]}; +function foo(arg: U): MyMap { + return arg; +}