diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 967ad86941b40..7aaf7bcae85d9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17196,7 +17196,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getStringMappingType(symbol: Symbol, type: Type): Type { return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) : 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)) : + type.flags & TypeFlags.TemplateLiteral ? applyTemplateStringMapping(symbol, type as TemplateLiteralType) : // Mapping> === Mapping type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type : type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.StringMapping) || isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) : @@ -17215,14 +17215,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return str; } - function applyTemplateStringMapping(symbol: Symbol, texts: readonly string[], types: readonly Type[]): [texts: readonly string[], types: readonly Type[]] { + function applyTemplateStringMapping(symbol: Symbol, type: TemplateLiteralType): Type { + const { texts, types } = type; switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { - case IntrinsicTypeKind.Uppercase: return [texts.map(t => t.toUpperCase()), types.map(t => getStringMappingType(symbol, t))]; - case IntrinsicTypeKind.Lowercase: return [texts.map(t => t.toLowerCase()), types.map(t => getStringMappingType(symbol, t))]; - case IntrinsicTypeKind.Capitalize: return [texts[0] === "" ? texts : [texts[0].charAt(0).toUpperCase() + texts[0].slice(1), ...texts.slice(1)], texts[0] === "" ? [getStringMappingType(symbol, types[0]), ...types.slice(1)] : types]; - case IntrinsicTypeKind.Uncapitalize: return [texts[0] === "" ? texts : [texts[0].charAt(0).toLowerCase() + texts[0].slice(1), ...texts.slice(1)], texts[0] === "" ? [getStringMappingType(symbol, types[0]), ...types.slice(1)] : types]; + case IntrinsicTypeKind.Uppercase: return getTemplateLiteralType(texts.map(t => t.toUpperCase()), types.map(t => getStringMappingType(symbol, t))); + case IntrinsicTypeKind.Lowercase: return getTemplateLiteralType(texts.map(t => t.toLowerCase()), types.map(t => getStringMappingType(symbol, t))); + case IntrinsicTypeKind.Capitalize: + case IntrinsicTypeKind.Uncapitalize: + return texts[0] === "" && types.length + ? types.length === 1 && texts[1] === "" + ? getStringMappingType(symbol, types[0]) + : getStringMappingTypeForGenericType(symbol, type) + : getTemplateLiteralType( + [applyStringMapping(symbol, texts[0]), ...texts.slice(1)], + types + ); } - return [texts, types]; + return getTemplateLiteralType(texts, types); } function getStringMappingTypeForGenericType(symbol: Symbol, type: Type): Type { diff --git a/tests/baselines/reference/genericStringMappings.symbols b/tests/baselines/reference/genericStringMappings.symbols new file mode 100644 index 0000000000000..cfcd5679806d0 --- /dev/null +++ b/tests/baselines/reference/genericStringMappings.symbols @@ -0,0 +1,81 @@ +//// [tests/cases/conformance/types/literal/genericStringMappings.ts] //// + +=== genericStringMappings.ts === +// repro from #52102#issuecomment-1371248589 + +type CamelCase1< +>CamelCase1 : Symbol(CamelCase1, Decl(genericStringMappings.ts, 0, 0)) + + S extends string, +>S : Symbol(S, Decl(genericStringMappings.ts, 2, 16)) + + L extends string = Lowercase, +>L : Symbol(L, Decl(genericStringMappings.ts, 3, 19)) +>Lowercase : Symbol(Lowercase, Decl(lib.es5.d.ts, --, --)) +>S : Symbol(S, Decl(genericStringMappings.ts, 2, 16)) + + Res extends string = "" +>Res : Symbol(Res, Decl(genericStringMappings.ts, 4, 34)) + +> = L extends "" +>L : Symbol(L, Decl(genericStringMappings.ts, 3, 19)) + + ? `start_${Uncapitalize}_end` +>Uncapitalize : Symbol(Uncapitalize, Decl(lib.es5.d.ts, --, --)) +>Res : Symbol(Res, Decl(genericStringMappings.ts, 4, 34)) + + : CamelCase1}`>; +>CamelCase1 : Symbol(CamelCase1, Decl(genericStringMappings.ts, 0, 0)) +>Res : Symbol(Res, Decl(genericStringMappings.ts, 4, 34)) +>Capitalize : Symbol(Capitalize, Decl(lib.es5.d.ts, --, --)) +>L : Symbol(L, Decl(genericStringMappings.ts, 3, 19)) + +type Test1 = CamelCase1<"ABC"> +>Test1 : Symbol(Test1, Decl(genericStringMappings.ts, 8, 52)) +>CamelCase1 : Symbol(CamelCase1, Decl(genericStringMappings.ts, 0, 0)) + +// repro from #52102 + +type CamelCase2< +>CamelCase2 : Symbol(CamelCase2, Decl(genericStringMappings.ts, 10, 30)) + + S extends string, +>S : Symbol(S, Decl(genericStringMappings.ts, 14, 16)) + + L extends string = Lowercase, +>L : Symbol(L, Decl(genericStringMappings.ts, 15, 19)) +>Lowercase : Symbol(Lowercase, Decl(lib.es5.d.ts, --, --)) +>S : Symbol(S, Decl(genericStringMappings.ts, 14, 16)) + + Res extends string = "" +>Res : Symbol(Res, Decl(genericStringMappings.ts, 16, 34)) + +> = L extends "" +>L : Symbol(L, Decl(genericStringMappings.ts, 15, 19)) + + ? Uncapitalize +>Uncapitalize : Symbol(Uncapitalize, Decl(lib.es5.d.ts, --, --)) +>Res : Symbol(Res, Decl(genericStringMappings.ts, 16, 34)) + + : L extends `${infer H}_${infer T}` +>L : Symbol(L, Decl(genericStringMappings.ts, 15, 19)) +>H : Symbol(H, Decl(genericStringMappings.ts, 20, 22)) +>T : Symbol(T, Decl(genericStringMappings.ts, 20, 33)) + + ? CamelCase2}`> +>CamelCase2 : Symbol(CamelCase2, Decl(genericStringMappings.ts, 10, 30)) +>T : Symbol(T, Decl(genericStringMappings.ts, 20, 33)) +>Res : Symbol(Res, Decl(genericStringMappings.ts, 16, 34)) +>Capitalize : Symbol(Capitalize, Decl(lib.es5.d.ts, --, --)) +>H : Symbol(H, Decl(genericStringMappings.ts, 20, 22)) + + : CamelCase2}`>; +>CamelCase2 : Symbol(CamelCase2, Decl(genericStringMappings.ts, 10, 30)) +>Res : Symbol(Res, Decl(genericStringMappings.ts, 16, 34)) +>Capitalize : Symbol(Capitalize, Decl(lib.es5.d.ts, --, --)) +>L : Symbol(L, Decl(genericStringMappings.ts, 15, 19)) + +type Test2 = CamelCase2<"FOOBAR"> +>Test2 : Symbol(Test2, Decl(genericStringMappings.ts, 22, 52)) +>CamelCase2 : Symbol(CamelCase2, Decl(genericStringMappings.ts, 10, 30)) + diff --git a/tests/baselines/reference/genericStringMappings.types b/tests/baselines/reference/genericStringMappings.types new file mode 100644 index 0000000000000..bef0bc3dd7cb4 --- /dev/null +++ b/tests/baselines/reference/genericStringMappings.types @@ -0,0 +1,35 @@ +//// [tests/cases/conformance/types/literal/genericStringMappings.ts] //// + +=== genericStringMappings.ts === +// repro from #52102#issuecomment-1371248589 + +type CamelCase1< +>CamelCase1 : CamelCase1 + + S extends string, + L extends string = Lowercase, + Res extends string = "" +> = L extends "" + ? `start_${Uncapitalize}_end` + : CamelCase1}`>; + +type Test1 = CamelCase1<"ABC"> +>Test1 : "start_abc_end" + +// repro from #52102 + +type CamelCase2< +>CamelCase2 : CamelCase2 + + S extends string, + L extends string = Lowercase, + Res extends string = "" +> = L extends "" + ? Uncapitalize + : L extends `${infer H}_${infer T}` + ? CamelCase2}`> + : CamelCase2}`>; + +type Test2 = CamelCase2<"FOOBAR"> +>Test2 : "foobar" + diff --git a/tests/cases/conformance/types/literal/genericStringMappings.ts b/tests/cases/conformance/types/literal/genericStringMappings.ts new file mode 100644 index 0000000000000..76752ad7b024b --- /dev/null +++ b/tests/cases/conformance/types/literal/genericStringMappings.ts @@ -0,0 +1,28 @@ +// @strict: true +// @noEmit: true + +// repro from #52102#issuecomment-1371248589 + +type CamelCase1< + S extends string, + L extends string = Lowercase, + Res extends string = "" +> = L extends "" + ? `start_${Uncapitalize}_end` + : CamelCase1}`>; + +type Test1 = CamelCase1<"ABC"> + +// repro from #52102 + +type CamelCase2< + S extends string, + L extends string = Lowercase, + Res extends string = "" +> = L extends "" + ? Uncapitalize + : L extends `${infer H}_${infer T}` + ? CamelCase2}`> + : CamelCase2}`>; + +type Test2 = CamelCase2<"FOOBAR"> \ No newline at end of file