Skip to content

Commit e38b70e

Browse files
Andaristdevanshj
andcommitted
Apply template string mappings on generic template literal types in a safe way for (Un)Capitalize
Co-authored-by: Devansh Jethmalani <[email protected]>
1 parent fc81abd commit e38b70e

File tree

4 files changed

+24
-15
lines changed

4 files changed

+24
-15
lines changed

src/compiler/checker.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16720,7 +16720,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1672016720
function getStringMappingType(symbol: Symbol, type: Type): Type {
1672116721
return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) :
1672216722
type.flags & TypeFlags.StringLiteral ? getStringLiteralType(applyStringMapping(symbol, (type as StringLiteralType).value)) :
16723-
type.flags & TypeFlags.TemplateLiteral && !isGenericType(type) ? getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)) :
16723+
type.flags & TypeFlags.TemplateLiteral ? applyTemplateStringMapping(symbol, type as TemplateLiteralType) :
1672416724
// Mapping<Mapping<T>> === Mapping<T>
1672516725
type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type :
1672616726
type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.StringMapping) || isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) :
@@ -16739,14 +16739,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1673916739
return str;
1674016740
}
1674116741

16742-
function applyTemplateStringMapping(symbol: Symbol, texts: readonly string[], types: readonly Type[]): [texts: readonly string[], types: readonly Type[]] {
16742+
function applyTemplateStringMapping(symbol: Symbol, type: TemplateLiteralType): Type {
16743+
const { texts, types } = type;
1674316744
switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {
16744-
case IntrinsicTypeKind.Uppercase: return [texts.map(t => t.toUpperCase()), types.map(t => getStringMappingType(symbol, t))];
16745-
case IntrinsicTypeKind.Lowercase: return [texts.map(t => t.toLowerCase()), types.map(t => getStringMappingType(symbol, t))];
16746-
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];
16747-
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];
16745+
case IntrinsicTypeKind.Uppercase: return getTemplateLiteralType(texts.map(t => t.toUpperCase()), types.map(t => getStringMappingType(symbol, t)));
16746+
case IntrinsicTypeKind.Lowercase: return getTemplateLiteralType(texts.map(t => t.toLowerCase()), types.map(t => getStringMappingType(symbol, t)));
16747+
case IntrinsicTypeKind.Capitalize:
16748+
case IntrinsicTypeKind.Uncapitalize:
16749+
return texts[0] === "" && types.length
16750+
? types.length === 1 && texts[1] === ""
16751+
? getStringMappingType(symbol, types[0])
16752+
: getStringMappingTypeForGenericType(symbol, type)
16753+
: getTemplateLiteralType(
16754+
[applyStringMapping(symbol, texts[0]), ...texts.slice(1)],
16755+
types
16756+
);
1674816757
}
16749-
return [texts, types];
16758+
return getTemplateLiteralType(texts, types);
1675016759
}
1675116760

1675216761
function getStringMappingTypeForGenericType(symbol: Symbol, type: Type): Type {

tests/baselines/reference/intrinsicTypes.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ type TN6 = Uncapitalize<42>; // Error
7272
>TN6 : 42
7373

7474
type TX1<S extends string> = Uppercase<`aB${S}`>;
75-
>TX1 : Uppercase<`aB${S}`>
75+
>TX1 : `AB${Uppercase<S>}`
7676

7777
type TX2 = TX1<'xYz'>; // "ABXYZ"
7878
>TX2 : "ABXYZ"
7979

8080
type TX3<S extends string> = Lowercase<`aB${S}`>;
81-
>TX3 : Lowercase<`aB${S}`>
81+
>TX3 : `ab${Lowercase<S>}`
8282

8383
type TX4 = TX3<'xYz'>; // "abxyz"
8484
>TX4 : "abxyz"

tests/baselines/reference/numericStringLiteralTypes.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type T3<T extends string> = string & `${T}`; // `${T}
1212
>T3 : `${T}`
1313

1414
type T4<T extends string> = string & `${Capitalize<`${T}`>}`; // `${Capitalize<T>}`
15-
>T4 : `${Capitalize<`${T}`>}`
15+
>T4 : `${Capitalize<T>}`
1616

1717
function f1(a: boolean[], x: `${number}`) {
1818
>f1 : (a: boolean[], x: `${number}`) => void

tests/baselines/reference/templateLiteralTypes3.types

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,8 @@ function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>,
568568
>ft1 : <T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) => void
569569
>t : T
570570
>u : Uppercase<T>
571-
>u1 : Uppercase<`1.${T}.3`>
572-
>u2 : Uppercase<`1.${T}.4`>
571+
>u1 : `1.${Uppercase<T>}.3`
572+
>u2 : `1.${Uppercase<T>}.4`
573573

574574
spread(`1.${t}.3`, `1.${t}.4`);
575575
>spread(`1.${t}.3`, `1.${t}.4`) : `1.${T}.3` | `1.${T}.4`
@@ -588,9 +588,9 @@ function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>,
588588
>u : Uppercase<T>
589589

590590
spread(u1, u2);
591-
>spread(u1, u2) : Uppercase<`1.${T}.3`> | Uppercase<`1.${T}.4`>
591+
>spread(u1, u2) : `1.${Uppercase<T>}.3` | `1.${Uppercase<T>}.4`
592592
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
593-
>u1 : Uppercase<`1.${T}.3`>
594-
>u2 : Uppercase<`1.${T}.4`>
593+
>u1 : `1.${Uppercase<T>}.3`
594+
>u2 : `1.${Uppercase<T>}.4`
595595
}
596596

0 commit comments

Comments
 (0)