Poznajemy lepiej typy tablicowe, słówko kluczowe infer oraz robimy coś mega, czyli piszemy nasz pierwszy typ rekurencyjny. Do dzieła.
Ok, zabawmy się indeksami:
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
type Person333 = typeof MyArray[number];
// type Person333 = {
// name: string;
// age: number;
// }
Mam nadzieję, że to nikogo nie dziwi. To teraz przykład łatwiejszy:
const AnotherArray = [1,2,3,4];
type Type333 = typeof AnotherArray[number];
//type Type333 = number
Typ number to typ indeksów tablic. W obiektach jest typ keyof any, którym obecnie jest number, string albo symbol, tablice mogą mieć tylko number, także ta składnia powinna nie być trudna do ogarnięcia.
Zresztą, jedno z drugim można łączyć:
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
type Person333age = typeof MyArray[number]["age"];
//typePerson333age = number
Ok, to napiszmy sobie flatten:
type Flatten<T> = T extends any[] ? T[number] : T;
Chyba proste, nie? Użycie:
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
const AnotherArray = [1,2,3,4];
type Flatten<T> = T extends any[] ? T[number] : T;
type Person444 = Flatten<typeof MyArray>
// type Person444 = {
// name: string;
// age: number;
// }
type Type444 = Flatten<typeof AnotherArray>
// type Type444 = number
Ok, to teraz zapiszemy to samo, ale tym razem ze słówkiem kluczowym infer, które już nieco poznaliśmy:
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
const AnotherArray = [1,2,3,4];
type Flatten<T> = T extends Array<infer NestedType> ? NestedType : T;
type Person444 = Flatten<typeof MyArray>
// type Person444 = {
// name: string;
// age: number;
// }
type Type444 = Flatten<typeof AnotherArray>
// type Type444 = number
Ok, działa, problem jest taki, że możemy mieć bardziej zagnieżdżone typy:
const nestedArr = [ [[1],[2],[3]], [[4],[5],[6]]];
type nestedArrType = typeof nestedArr;
//type nestedArrType = number[][][]
I co nam tu flatten zrobi? Po prostu jeden [] zabierze:
type flat = Flatten<nestedArrType>
//type flat = number[][]
Rekurencyjne typy są łatwiejsze, niż się wydają:
type FlattenNested<T> = T extends Array<infer Nested> ? FlattenNested<Nested> : T;
Jak rozumiemy, co robi infer, nie powinniśmy mieć żadnego problemu z nimi:
type FlattenNested<T> = T extends Array<infer Nested> ? FlattenNested<Nested> : T;
const nestedArr = [ [[1],[2],[3]], [[4],[5],[6]]];
type nestedArrType = typeof nestedArr;
//type nestedArrType = number[][][]
type nestedArrFlatType = FlattenNested<nestedArrType>
//type nestedArrFlatType = number
Oczywiście po internecie krążą inne przykłady rekurencyjnych typów, które wcale łatwe nie są, np. typ Strlen, który podaje jako typ zwracany konkretną liczbę, będącą tego napisu długością.
I to zapewne warto poznawać, warto próbować osiągnąć mistrzostwo w TS, aczkolwiek chyba można śmiało powiedzieć, że rekurencyjne typy w TS są zazwyczaj bardzo, bardzo łatwe.