Extrayendo el Último Carácter de un Literal de Cadena en TypeScript
¿Es posible extraer el último carácter de una cadena? Ahora mismo lo estoy haciendo con dígitos numéricos, así que no necesito escribir todo el alfabeto como un enumerado.
También estoy algo confundido sobre por qué lo que escribí no está funcionando correctamente.
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0';
type GetFirstDigitOfStringLiteralNumber<T extends `${string}${Digit}`> = T extends `${infer D extends Digit}${string}` ? D : never;
type GetLastDigitOfStringLiteralNumber<T extends `${string}${Digit}`> = T extends `${string}${infer D extends Digit}` ? D : never;
// This is correct
type FirstCharacter = GetFirstDigitOfStringLiteralNumber<"21222">;
// ^? "2"
// This is correct
type LastCharacter1 = GetLastDigitOfStringLiteralNumber<"12">;
// ^? "2"
// This is incorrect
type LastCharacter2 = GetLastDigitOfStringLiteralNumber<"21222">;
// ^? '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0'
Parque infantil
El LastChracter2 que no funciona realmente me sorprende. Lo que supongo que está ocurriendo es que TypeScript no determina correctamente cuál es la parte $ {string}, y abandona el análisis estático debido a eso.
Pero eso no me explica por qué simplemente no devuelve never en el caso de que no pueda hacer coincidir la parte Digit con un carácter específico.
Editar :
Dar a la porción $ {string} un nombre inferido parece causar el resultado never que esperaba, a pesar de no cambiar nada más:
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0';
type GetLastDigitOfStringLiteralNumber<T extends `${string}${Digit}`> = T extends `${infer L extends string}${infer D extends Digit}` ? D : never;
// This is working
type InferLeading = GetLastDigitOfStringLiteralNumber<"12">;
// ^? "2"
// Why is this breaking?
type InferLeading2 = GetLastDigitOfStringLiteralNumber<"112">;
// ^? never
Respuesta #1
Esto no funciona porque usar $ {infer L}$ {infer R} en una cadena de más de dos caracteres siempre inferirá el primer carácter como L y el resto como R. Así que para "123456", L sería "1" y R sería "23456". Comprobar si R extends Digit conduce a un comportamiento incorrecto si R no es un solo carácter.
Para solucionar esto, puedes hacer que el tipo sea recursivo.
type GetLastDigitOfStringLiteralNumber<T extends `${string}${Digit}`> =
T extends `${string}${infer REST extends `${infer L}${Digit}`}`
? GetLastDigitOfStringLiteralNumber<REST>
: T
Parque infantil
