Vyhledávání na webu

Typescript: deklarovat typ indexu n-tice?


Vzhledem k TypeScript Tuple, například:

const arr = [1, 2] as const;

Dostáváme statické kontroly typů při indexování:

console.log(arr[1]);

je dobré, ale

console.log(arr[2]);

chyby s:

Typ n-tice 'readonly [1, 2]' délky '2' nemá prvek na indexu '2'.ts (2493)

co je skvělé.


Rád bych deklaroval konstantu jako typ indexu toho n-tice, aby to, co lze přiřadit té konstantě, dodržovalo stejná omezení (0 | 1).

Zkoušel jsem to:

const index: keyof typeof arr = 2 as const;
console.log(arr[index]);

ale TypeScript nezobrazil žádné chyby. Mám podezření, že index je number, což je příliš obecné.

Odpověď č. 1

Jo, z nějakého důvodu jsou indexy tuple typy číselných řetězců jako "0" a "1" místo odpovídajících číselných literálních typů jako 0 a 1. Také existuje number indexový podpis, takže keyof ["a", "b"] vám dá number | "0" | "1" | .... To znamená, že pouhé použití keyof vám umožní přiřadit libovolné číslo.

Pokud chcete vypočítat číselné literály, můžete k tomu použít typy šablonových literálů, alespoň v TypeScriptu 4.8 a novějším:

type TupleIndices<T extends readonly any[]> =
    Extract<keyof T, `${number}`> extends `${infer N extends number}` ? N : never;

Nejprve vytáhnu typy číselných textových řetězců z celé množiny klíčů filtrováním podle `$ {number}`, tedy množiny všech řetězců, které lze zpracovat jako číslo. To eliminuje samotné number (což není řetězec) a také "length" a "slice" a další členy pole. Takto dostaneme "0" | "1" | "2" | .... Poté použiji vylepšené typy infer představené v TypeScriptu 4.8 k převodu těchto textových literálů na číselné literály.

To vám dává toto:

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

Odkaz na kód na playgroundu

Související články