diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e4f940238cd3..9ebaca719f2b9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12073,7 +12073,20 @@ namespace ts { } function isGenericMappedType(type: Type): type is MappedType { - return !!(getObjectFlags(type) & ObjectFlags.Mapped) && isGenericIndexType(getConstraintTypeFromMappedType(type as MappedType)); + if (getObjectFlags(type) & ObjectFlags.Mapped) { + const constraint = getConstraintTypeFromMappedType(type as MappedType); + if (isGenericIndexType(constraint)) { + return true; + } + // A mapped type is generic if the 'as' clause references generic types other than the iteration type. + // To determine this, we substitute the constraint type (that we now know isn't generic) for the iteration + // type and check whether the resulting type is generic. + const nameType = getNameTypeFromMappedType(type as MappedType); + if (nameType && isGenericIndexType(instantiateType(nameType, makeUnaryTypeMapper(getTypeParameterFromMappedType(type as MappedType), constraint)))) { + return true; + } + } + return false; } function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { @@ -15638,8 +15651,14 @@ namespace ts { return getStringLiteralType(text); } newTexts.push(text); - if (every(newTexts, t => t === "") && every(newTypes, t => !!(t.flags & TypeFlags.String))) { - return stringType; + if (every(newTexts, t => t === "")) { + if (every(newTypes, t => !!(t.flags & TypeFlags.String))) { + return stringType; + } + // Normalize `${Mapping}` into Mapping + if (newTypes.length === 1 && isPatternLiteralType(newTypes[0])) { + return newTypes[0]; + } } const id = `${getTypeListId(newTypes)}|${map(newTexts, t => t.length).join(",")}|${newTexts.join("")}`; let type = templateLiteralTypes.get(id); @@ -15698,11 +15717,13 @@ namespace ts { function getStringMappingType(symbol: Symbol, type: Type): Type { return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) : - // Mapping> === Mapping - type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type : - isGenericIndexType(type) || isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, isPatternLiteralPlaceholderType(type) && !(type.flags & TypeFlags.StringMapping) ? getTemplateLiteralType(["", ""], [type]) : type) : type.flags & TypeFlags.StringLiteral ? getStringLiteralType(applyStringMapping(symbol, (type as StringLiteralType).value)) : type.flags & TypeFlags.TemplateLiteral ? getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)) : + // Mapping> === Mapping + type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type : + type.flags & (TypeFlags.Any | TypeFlags.String || type.flags & TypeFlags.StringMapping) || isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) : + // This handles Mapping<`${number}`> and Mapping<`${bigint}`> + isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, getTemplateLiteralType(["", ""], [type])) : type; } @@ -16000,11 +16021,12 @@ namespace ts { } function isPatternLiteralPlaceholderType(type: Type): boolean { - return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || !!(type.flags & TypeFlags.StringMapping && isPatternLiteralPlaceholderType((type as StringMappingType).type)); + return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || isPatternLiteralType(type); } function isPatternLiteralType(type: Type) { - return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType); + return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType) || + !!(type.flags & TypeFlags.StringMapping) && isPatternLiteralPlaceholderType((type as StringMappingType).type); } function isGenericType(type: Type): boolean { @@ -22559,7 +22581,7 @@ namespace ts { } function isMemberOfStringMapping(source: Type, target: Type): boolean { - if (target.flags & (TypeFlags.String | TypeFlags.AnyOrUnknown)) { + if (target.flags & (TypeFlags.String | TypeFlags.Any)) { return true; } if (target.flags & TypeFlags.TemplateLiteral) { diff --git a/tests/baselines/reference/genericMappedTypeAsClause.errors.txt b/tests/baselines/reference/genericMappedTypeAsClause.errors.txt new file mode 100644 index 0000000000000..35c7395979cf1 --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.errors.txt @@ -0,0 +1,48 @@ +tests/cases/compiler/genericMappedTypeAsClause.ts(11,36): error TS2322: Type 'string' is not assignable to type 'number'. +tests/cases/compiler/genericMappedTypeAsClause.ts(14,11): error TS2322: Type 'number' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(15,11): error TS2322: Type 'string' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(16,11): error TS2322: Type 'number[]' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(17,11): error TS2322: Type 'boolean' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(18,34): error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'MappedModel'. + Object literal may only specify known properties, and 'a' does not exist in type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(19,11): error TS2322: Type 'undefined' is not assignable to type 'MappedModel'. + + +==== tests/cases/compiler/genericMappedTypeAsClause.ts (7 errors) ==== + type Model = { + a: string; + b: number; + }; + + type MappedModel = { + [K in keyof Model as `${K}${Suffix}`]: Model[K]; + }; + + const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; + const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error + ~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. +!!! related TS6500 tests/cases/compiler/genericMappedTypeAsClause.ts:6:43: The expected type comes from property 'bFoo' which is declared here on type 'MappedModel<"Foo">' + + function f1() { + const x1: MappedModel = 42; // Error + ~~ +!!! error TS2322: Type 'number' is not assignable to type 'MappedModel'. + const x2: MappedModel = 'test'; // Error + ~~ +!!! error TS2322: Type 'string' is not assignable to type 'MappedModel'. + const x3: MappedModel = [1, 2, 3]; // Error + ~~ +!!! error TS2322: Type 'number[]' is not assignable to type 'MappedModel'. + const x4: MappedModel = false; // Error + ~~ +!!! error TS2322: Type 'boolean' is not assignable to type 'MappedModel'. + const x5: MappedModel = { a: 'bar', b: 42 }; // Error + ~~~~~~~~ +!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'MappedModel'. +!!! error TS2322: Object literal may only specify known properties, and 'a' does not exist in type 'MappedModel'. + const x6: MappedModel = undefined; // Error + ~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'MappedModel'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/genericMappedTypeAsClause.js b/tests/baselines/reference/genericMappedTypeAsClause.js new file mode 100644 index 0000000000000..0e11eb753995a --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.js @@ -0,0 +1,35 @@ +//// [genericMappedTypeAsClause.ts] +type Model = { + a: string; + b: number; +}; + +type MappedModel = { + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error + +function f1() { + const x1: MappedModel = 42; // Error + const x2: MappedModel = 'test'; // Error + const x3: MappedModel = [1, 2, 3]; // Error + const x4: MappedModel = false; // Error + const x5: MappedModel = { a: 'bar', b: 42 }; // Error + const x6: MappedModel = undefined; // Error +} + + +//// [genericMappedTypeAsClause.js] +"use strict"; +var foo1 = { aFoo: 'test', bFoo: 42 }; +var foo2 = { bFoo: 'bar' }; // Error +function f1() { + var x1 = 42; // Error + var x2 = 'test'; // Error + var x3 = [1, 2, 3]; // Error + var x4 = false; // Error + var x5 = { a: 'bar', b: 42 }; // Error + var x6 = undefined; // Error +} diff --git a/tests/baselines/reference/genericMappedTypeAsClause.symbols b/tests/baselines/reference/genericMappedTypeAsClause.symbols new file mode 100644 index 0000000000000..c0d563564cd88 --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.symbols @@ -0,0 +1,75 @@ +=== tests/cases/compiler/genericMappedTypeAsClause.ts === +type Model = { +>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0)) + + a: string; +>a : Symbol(a, Decl(genericMappedTypeAsClause.ts, 0, 14)) + + b: number; +>b : Symbol(b, Decl(genericMappedTypeAsClause.ts, 1, 14)) + +}; + +type MappedModel = { +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>Suffix : Symbol(Suffix, Decl(genericMappedTypeAsClause.ts, 5, 17)) + + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5)) +>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0)) +>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5)) +>Suffix : Symbol(Suffix, Decl(genericMappedTypeAsClause.ts, 5, 17)) +>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0)) +>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5)) + +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +>foo1 : Symbol(foo1, Decl(genericMappedTypeAsClause.ts, 9, 5)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>aFoo : Symbol(aFoo, Decl(genericMappedTypeAsClause.ts, 9, 34)) +>bFoo : Symbol(bFoo, Decl(genericMappedTypeAsClause.ts, 9, 48)) + +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error +>foo2 : Symbol(foo2, Decl(genericMappedTypeAsClause.ts, 10, 5)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>bFoo : Symbol(bFoo, Decl(genericMappedTypeAsClause.ts, 10, 34)) + +function f1() { +>f1 : Symbol(f1, Decl(genericMappedTypeAsClause.ts, 10, 49)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x1: MappedModel = 42; // Error +>x1 : Symbol(x1, Decl(genericMappedTypeAsClause.ts, 13, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x2: MappedModel = 'test'; // Error +>x2 : Symbol(x2, Decl(genericMappedTypeAsClause.ts, 14, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x3: MappedModel = [1, 2, 3]; // Error +>x3 : Symbol(x3, Decl(genericMappedTypeAsClause.ts, 15, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x4: MappedModel = false; // Error +>x4 : Symbol(x4, Decl(genericMappedTypeAsClause.ts, 16, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x5: MappedModel = { a: 'bar', b: 42 }; // Error +>x5 : Symbol(x5, Decl(genericMappedTypeAsClause.ts, 17, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) +>a : Symbol(a, Decl(genericMappedTypeAsClause.ts, 17, 32)) +>b : Symbol(b, Decl(genericMappedTypeAsClause.ts, 17, 42)) + + const x6: MappedModel = undefined; // Error +>x6 : Symbol(x6, Decl(genericMappedTypeAsClause.ts, 18, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) +>undefined : Symbol(undefined) +} + diff --git a/tests/baselines/reference/genericMappedTypeAsClause.types b/tests/baselines/reference/genericMappedTypeAsClause.types new file mode 100644 index 0000000000000..478011afea431 --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/genericMappedTypeAsClause.ts === +type Model = { +>Model : { a: string; b: number; } + + a: string; +>a : string + + b: number; +>b : number + +}; + +type MappedModel = { +>MappedModel : MappedModel + + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +>foo1 : MappedModel<"Foo"> +>{ aFoo: 'test', bFoo: 42 } : { aFoo: string; bFoo: number; } +>aFoo : string +>'test' : "test" +>bFoo : number +>42 : 42 + +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error +>foo2 : MappedModel<"Foo"> +>{ bFoo: 'bar' } : { bFoo: string; } +>bFoo : string +>'bar' : "bar" + +function f1() { +>f1 : () => void + + const x1: MappedModel = 42; // Error +>x1 : MappedModel +>42 : 42 + + const x2: MappedModel = 'test'; // Error +>x2 : MappedModel +>'test' : "test" + + const x3: MappedModel = [1, 2, 3]; // Error +>x3 : MappedModel +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 + + const x4: MappedModel = false; // Error +>x4 : MappedModel +>false : false + + const x5: MappedModel = { a: 'bar', b: 42 }; // Error +>x5 : MappedModel +>{ a: 'bar', b: 42 } : { a: string; b: number; } +>a : string +>'bar' : "bar" +>b : number +>42 : 42 + + const x6: MappedModel = undefined; // Error +>x6 : MappedModel +>undefined : undefined +} + diff --git a/tests/baselines/reference/intrinsicTypes.types b/tests/baselines/reference/intrinsicTypes.types index d17e37284c10b..b054269683432 100644 --- a/tests/baselines/reference/intrinsicTypes.types +++ b/tests/baselines/reference/intrinsicTypes.types @@ -9,7 +9,7 @@ type TU3 = Uppercase; // Uppercase >TU3 : Uppercase type TU4 = Uppercase; // Uppercase<`${any}`> ->TU4 : Uppercase<`${any}`> +>TU4 : Uppercase type TU5 = Uppercase; // never >TU5 : never @@ -27,7 +27,7 @@ type TL3 = Lowercase; // Lowercase >TL3 : Lowercase type TL4 = Lowercase; // Lowercase<`${any}`> ->TL4 : Lowercase<`${any}`> +>TL4 : Lowercase type TL5 = Lowercase; // never >TL5 : never @@ -45,7 +45,7 @@ type TC3 = Capitalize; // Capitalize >TC3 : Capitalize type TC4 = Capitalize; // Capitalize<`${any}`> ->TC4 : Capitalize<`${any}`> +>TC4 : Capitalize type TC5 = Capitalize; // never >TC5 : never @@ -63,7 +63,7 @@ type TN3 = Uncapitalize; // Uncapitalize >TN3 : Uncapitalize type TN4 = Uncapitalize; // Uncapitalize<`${any}`> ->TN4 : Uncapitalize<`${any}`> +>TN4 : Uncapitalize type TN5 = Uncapitalize; // never >TN5 : never @@ -72,13 +72,13 @@ type TN6 = Uncapitalize<42>; // Error >TN6 : 42 type TX1 = Uppercase<`aB${S}`>; ->TX1 : Uppercase<`aB${S}`> +>TX1 : `AB${Uppercase}` type TX2 = TX1<'xYz'>; // "ABXYZ" >TX2 : "ABXYZ" type TX3 = Lowercase<`aB${S}`>; ->TX3 : Lowercase<`aB${S}`> +>TX3 : `ab${Lowercase}` type TX4 = TX3<'xYz'>; // "abxyz" >TX4 : "abxyz" diff --git a/tests/baselines/reference/mappedTypeAsClauses.types b/tests/baselines/reference/mappedTypeAsClauses.types index de951157933a3..31ee234f7daf4 100644 --- a/tests/baselines/reference/mappedTypeAsClauses.types +++ b/tests/baselines/reference/mappedTypeAsClauses.types @@ -61,7 +61,7 @@ type TD1 = DoubleProp<{ a: string, b: number }>; // { a1: string, a2: string, b >b : number type TD2 = keyof TD1; // 'a1' | 'a2' | 'b1' | 'b2' ->TD2 : "a1" | "a2" | "b1" | "b2" +>TD2 : "a1" | "b1" | "a2" | "b2" type TD3 = keyof DoubleProp; // `${keyof U & string}1` | `${keyof U & string}2` >TD3 : `${keyof U & string}1` | `${keyof U & string}2` diff --git a/tests/baselines/reference/numericStringLiteralTypes.types b/tests/baselines/reference/numericStringLiteralTypes.types index 245a3a674ba8e..e22186758f727 100644 --- a/tests/baselines/reference/numericStringLiteralTypes.types +++ b/tests/baselines/reference/numericStringLiteralTypes.types @@ -12,7 +12,7 @@ type T3 = string & `${T}`; // `${T} >T3 : `${T}` type T4 = string & `${Capitalize<`${T}`>}`; // `${Capitalize}` ->T4 : `${Capitalize<`${T}`>}` +>T4 : `${Capitalize}` function f1(a: boolean[], x: `${number}`) { >f1 : (a: boolean[], x: `${number}`) => void diff --git a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt index 63890e6891284..cf74e17c86349 100644 --- a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt +++ b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(7,1): error TS2322: Type 'string' is not assignable to type 'Uppercase>'. -tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(15,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. -tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(16,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. +tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(15,1): error TS2322: Type 'string' is not assignable to type 'Uppercase>'. +tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(16,1): error TS2322: Type 'string' is not assignable to type 'Uppercase>'. ==== tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts (3 errors) ==== @@ -22,7 +22,7 @@ tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts( // bad y = "a"; ~ -!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. +!!! error TS2322: Type 'string' is not assignable to type 'Uppercase>'. y = "A"; ~ -!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. \ No newline at end of file +!!! error TS2322: Type 'string' is not assignable to type 'Uppercase>'. \ No newline at end of file diff --git a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types index 34e7963a8e662..852c4ca0d426f 100644 --- a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types +++ b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types @@ -15,22 +15,22 @@ x = "a"; >"a" : "a" declare var y: Uppercase>; ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> // good y = "1"; >y = "1" : "1" ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> >"1" : "1" // bad y = "a"; >y = "a" : "a" ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> >"a" : "a" y = "A"; >y = "A" : "A" ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> >"A" : "A" diff --git a/tests/baselines/reference/templateLiteralTypes3.types b/tests/baselines/reference/templateLiteralTypes3.types index 66a36048f07af..3fb7144050bcf 100644 --- a/tests/baselines/reference/templateLiteralTypes3.types +++ b/tests/baselines/reference/templateLiteralTypes3.types @@ -568,8 +568,8 @@ function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, >ft1 : (t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) => void >t : T >u : Uppercase ->u1 : Uppercase<`1.${T}.3`> ->u2 : Uppercase<`1.${T}.4`> +>u1 : `1.${Uppercase}.3` +>u2 : `1.${Uppercase}.4` spread(`1.${t}.3`, `1.${t}.4`); >spread(`1.${t}.3`, `1.${t}.4`) : `1.${T}.3` | `1.${T}.4` @@ -588,9 +588,9 @@ function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, >u : Uppercase spread(u1, u2); ->spread(u1, u2) : Uppercase<`1.${T}.3`> | Uppercase<`1.${T}.4`> +>spread(u1, u2) : `1.${Uppercase}.3` | `1.${Uppercase}.4` >spread :

(...args: P[]) => P ->u1 : Uppercase<`1.${T}.3`> ->u2 : Uppercase<`1.${T}.4`> +>u1 : `1.${Uppercase}.3` +>u2 : `1.${Uppercase}.4` } diff --git a/tests/cases/compiler/genericMappedTypeAsClause.ts b/tests/cases/compiler/genericMappedTypeAsClause.ts new file mode 100644 index 0000000000000..a5b965a02d952 --- /dev/null +++ b/tests/cases/compiler/genericMappedTypeAsClause.ts @@ -0,0 +1,22 @@ +// @strict: true + +type Model = { + a: string; + b: number; +}; + +type MappedModel = { + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error + +function f1() { + const x1: MappedModel = 42; // Error + const x2: MappedModel = 'test'; // Error + const x3: MappedModel = [1, 2, 3]; // Error + const x4: MappedModel = false; // Error + const x5: MappedModel = { a: 'bar', b: 42 }; // Error + const x6: MappedModel = undefined; // Error +}