Typescript: ¿declarar el tipo del índice de una tupla?
Dado un Tuple de TypeScript, por ejemplo:
const arr = [1, 2] as const;
Obtenemos verificaciones de tipo estático al indexar:
console.log(arr[1]);
es bueno, pero
console.log(arr[2]);
errores con:
El tipo de tupla 'readonly [1, 2]' de longitud '2' no tiene un elemento en el índice '2'.ts (2493)
lo cual es genial.
Me gustaría declarar una constante para que sea del tipo del índice de esa tupla, de modo que lo que se pueda asignar a esa constante siga las mismas restricciones (0 | 1).
Intenté esto:
const index: keyof typeof arr = 2 as const;
console.log(arr[index]);
pero TypeScript no mostró ningún error. Sospecho que index es un number que es demasiado amplio.
Respuesta #1
Sí, por alguna razón, los índices de tupla son tipos de literales de cadena numérica como "0" y "1" en lugar de los correspondientes tipos de literales numéricos como 0 y 1. También hay una firma de índice number, así que keyof ["a", "b"] te dará number | "0" | "1" | .... Lo que significa que solo usar keyof te permitirá asignar cualquier número.
Si quieres calcular los literales numéricos, puedes usar tipos de literales de plantilla para hacerlo, al menos en TypeScript 4.8 y superiores:
type TupleIndices<T extends readonly any[]> =
Extract<keyof T, `${number}`> extends `${infer N extends number}` ? N : never;
Primero Extraigo los tipos literales de cadena numérica del conjunto completo de claves filtrando sobre `$ {number}`, el conjunto de todas las cadenas que se pueden analizar como un número. Esto elimina number en sí (que no es una cadena) y también "length" y "slice" y otros miembros de array. Así que eso nos da "0" | "1" | "2" | .... Luego uso los tipos mejorados infer introducidos en TypeScript 4.8 para convertir esos literales de cadena en literales numéricos.
Eso te da esto:
const arr = [1, 2] as const;
type ArrIndices = TupleIndices<typeof arr>;
// type ArrIndices = 0 | 1
type Also = TupleIndices<[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]>;
// type Also = 0 | 1 | 2 | 9 | 3 | 4 | 5 | 6 | 7 | 8
Enlace de Playground al código
