From 39ccac654c9836c94ffa2fe73fcaa20967e5a943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 8 Nov 2022 01:22:27 +0100 Subject: [PATCH] Add known keys of the mapped type constraint to its members (#50081) * Add known keys of the mapped type constraint to its members * Avoid second pass of adding known members by instead passing `noReductions` to `mapType` --- src/compiler/checker.ts | 2 +- .../mappedTypeGenericWithKnownKeys.errors.txt | 21 +++++++++ .../mappedTypeGenericWithKnownKeys.symbols | 33 ++++++++++++++ .../mappedTypeGenericWithKnownKeys.types | 43 +++++++++++++++++++ .../mappedTypeGenericWithKnownKeys.ts | 14 ++++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/mappedTypeGenericWithKnownKeys.errors.txt create mode 100644 tests/baselines/reference/mappedTypeGenericWithKnownKeys.symbols create mode 100644 tests/baselines/reference/mappedTypeGenericWithKnownKeys.types create mode 100644 tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b293a001029..310dbacba3a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12021,7 +12021,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type; } if (type.flags & TypeFlags.Union) { - return mapType(type as UnionType, getLowerBoundOfKeyType); + return mapType(type as UnionType, getLowerBoundOfKeyType, /*noReductions*/ true); } if (type.flags & TypeFlags.Intersection) { // Similarly to getTypeFromIntersectionTypeNode, we preserve the special string & {}, number & {}, diff --git a/tests/baselines/reference/mappedTypeGenericWithKnownKeys.errors.txt b/tests/baselines/reference/mappedTypeGenericWithKnownKeys.errors.txt new file mode 100644 index 00000000000..8a3c08defdc --- /dev/null +++ b/tests/baselines/reference/mappedTypeGenericWithKnownKeys.errors.txt @@ -0,0 +1,21 @@ +tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts(9,9): error TS2551: Property 'unknownLiteralKey' does not exist on type 'Record'. Did you mean 'knownLiteralKey'? +tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts(10,5): error TS2536: Type 'string' cannot be used to index type 'Record'. + + +==== tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts (2 errors) ==== + // based on #50054 + + function test>(shape: Shape, key: keyof Shape) { + const obj = {} as Record; + + obj.knownLiteralKey = 1; + obj[key] = 2; + + obj.unknownLiteralKey = 3; // error + ~~~~~~~~~~~~~~~~~ +!!! error TS2551: Property 'unknownLiteralKey' does not exist on type 'Record'. Did you mean 'knownLiteralKey'? + obj['' as string] = 4; // error + ~~~~~~~~~~~~~~~~~ +!!! error TS2536: Type 'string' cannot be used to index type 'Record'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeGenericWithKnownKeys.symbols b/tests/baselines/reference/mappedTypeGenericWithKnownKeys.symbols new file mode 100644 index 00000000000..61896c85b0f --- /dev/null +++ b/tests/baselines/reference/mappedTypeGenericWithKnownKeys.symbols @@ -0,0 +1,33 @@ +=== tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts === +// based on #50054 + +function test>(shape: Shape, key: keyof Shape) { +>test : Symbol(test, Decl(mappedTypeGenericWithKnownKeys.ts, 0, 0)) +>Shape : Symbol(Shape, Decl(mappedTypeGenericWithKnownKeys.ts, 2, 14)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>shape : Symbol(shape, Decl(mappedTypeGenericWithKnownKeys.ts, 2, 52)) +>Shape : Symbol(Shape, Decl(mappedTypeGenericWithKnownKeys.ts, 2, 14)) +>key : Symbol(key, Decl(mappedTypeGenericWithKnownKeys.ts, 2, 65)) +>Shape : Symbol(Shape, Decl(mappedTypeGenericWithKnownKeys.ts, 2, 14)) + + const obj = {} as Record; +>obj : Symbol(obj, Decl(mappedTypeGenericWithKnownKeys.ts, 3, 9)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Shape : Symbol(Shape, Decl(mappedTypeGenericWithKnownKeys.ts, 2, 14)) + + obj.knownLiteralKey = 1; +>obj.knownLiteralKey : Symbol(knownLiteralKey) +>obj : Symbol(obj, Decl(mappedTypeGenericWithKnownKeys.ts, 3, 9)) +>knownLiteralKey : Symbol(knownLiteralKey) + + obj[key] = 2; +>obj : Symbol(obj, Decl(mappedTypeGenericWithKnownKeys.ts, 3, 9)) +>key : Symbol(key, Decl(mappedTypeGenericWithKnownKeys.ts, 2, 65)) + + obj.unknownLiteralKey = 3; // error +>obj : Symbol(obj, Decl(mappedTypeGenericWithKnownKeys.ts, 3, 9)) + + obj['' as string] = 4; // error +>obj : Symbol(obj, Decl(mappedTypeGenericWithKnownKeys.ts, 3, 9)) +} + diff --git a/tests/baselines/reference/mappedTypeGenericWithKnownKeys.types b/tests/baselines/reference/mappedTypeGenericWithKnownKeys.types new file mode 100644 index 00000000000..1f0875e55ba --- /dev/null +++ b/tests/baselines/reference/mappedTypeGenericWithKnownKeys.types @@ -0,0 +1,43 @@ +=== tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts === +// based on #50054 + +function test>(shape: Shape, key: keyof Shape) { +>test : >(shape: Shape, key: keyof Shape) => void +>shape : Shape +>key : keyof Shape + + const obj = {} as Record; +>obj : Record +>{} as Record : Record +>{} : {} + + obj.knownLiteralKey = 1; +>obj.knownLiteralKey = 1 : 1 +>obj.knownLiteralKey : number +>obj : Record +>knownLiteralKey : number +>1 : 1 + + obj[key] = 2; +>obj[key] = 2 : 2 +>obj[key] : Record[keyof Shape] +>obj : Record +>key : keyof Shape +>2 : 2 + + obj.unknownLiteralKey = 3; // error +>obj.unknownLiteralKey = 3 : 3 +>obj.unknownLiteralKey : any +>obj : Record +>unknownLiteralKey : any +>3 : 3 + + obj['' as string] = 4; // error +>obj['' as string] = 4 : 4 +>obj['' as string] : any +>obj : Record +>'' as string : string +>'' : "" +>4 : 4 +} + diff --git a/tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts b/tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts new file mode 100644 index 00000000000..315caaab2b2 --- /dev/null +++ b/tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts @@ -0,0 +1,14 @@ +// @noEmit: true +// @strict: true + +// based on #50054 + +function test>(shape: Shape, key: keyof Shape) { + const obj = {} as Record; + + obj.knownLiteralKey = 1; + obj[key] = 2; + + obj.unknownLiteralKey = 3; // error + obj['' as string] = 4; // error +}