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`
This commit is contained in:
Mateusz Burzyński 2022-11-08 01:22:27 +01:00 committed by GitHub
parent 48aebcdcdb
commit 39ccac654c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 1 deletions

View File

@ -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 & {},

View File

@ -0,0 +1,21 @@
tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts(9,9): error TS2551: Property 'unknownLiteralKey' does not exist on type 'Record<keyof Shape | "knownLiteralKey", number>'. Did you mean 'knownLiteralKey'?
tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts(10,5): error TS2536: Type 'string' cannot be used to index type 'Record<keyof Shape | "knownLiteralKey", number>'.
==== tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts (2 errors) ====
// based on #50054
function test<Shape extends Record<string, string>>(shape: Shape, key: keyof Shape) {
const obj = {} as Record<keyof Shape | "knownLiteralKey", number>;
obj.knownLiteralKey = 1;
obj[key] = 2;
obj.unknownLiteralKey = 3; // error
~~~~~~~~~~~~~~~~~
!!! error TS2551: Property 'unknownLiteralKey' does not exist on type 'Record<keyof Shape | "knownLiteralKey", number>'. Did you mean 'knownLiteralKey'?
obj['' as string] = 4; // error
~~~~~~~~~~~~~~~~~
!!! error TS2536: Type 'string' cannot be used to index type 'Record<keyof Shape | "knownLiteralKey", number>'.
}

View File

@ -0,0 +1,33 @@
=== tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts ===
// based on #50054
function test<Shape extends Record<string, string>>(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<keyof Shape | "knownLiteralKey", number>;
>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))
}

View File

@ -0,0 +1,43 @@
=== tests/cases/compiler/mappedTypeGenericWithKnownKeys.ts ===
// based on #50054
function test<Shape extends Record<string, string>>(shape: Shape, key: keyof Shape) {
>test : <Shape extends Record<string, string>>(shape: Shape, key: keyof Shape) => void
>shape : Shape
>key : keyof Shape
const obj = {} as Record<keyof Shape | "knownLiteralKey", number>;
>obj : Record<keyof Shape | "knownLiteralKey", number>
>{} as Record<keyof Shape | "knownLiteralKey", number> : Record<keyof Shape | "knownLiteralKey", number>
>{} : {}
obj.knownLiteralKey = 1;
>obj.knownLiteralKey = 1 : 1
>obj.knownLiteralKey : number
>obj : Record<keyof Shape | "knownLiteralKey", number>
>knownLiteralKey : number
>1 : 1
obj[key] = 2;
>obj[key] = 2 : 2
>obj[key] : Record<keyof Shape | "knownLiteralKey", number>[keyof Shape]
>obj : Record<keyof Shape | "knownLiteralKey", number>
>key : keyof Shape
>2 : 2
obj.unknownLiteralKey = 3; // error
>obj.unknownLiteralKey = 3 : 3
>obj.unknownLiteralKey : any
>obj : Record<keyof Shape | "knownLiteralKey", number>
>unknownLiteralKey : any
>3 : 3
obj['' as string] = 4; // error
>obj['' as string] = 4 : 4
>obj['' as string] : any
>obj : Record<keyof Shape | "knownLiteralKey", number>
>'' as string : string
>'' : ""
>4 : 4
}

View File

@ -0,0 +1,14 @@
// @noEmit: true
// @strict: true
// based on #50054
function test<Shape extends Record<string, string>>(shape: Shape, key: keyof Shape) {
const obj = {} as Record<keyof Shape | "knownLiteralKey", number>;
obj.knownLiteralKey = 1;
obj[key] = 2;
obj.unknownLiteralKey = 3; // error
obj['' as string] = 4; // error
}