Kilka ciekawych i nieco już bardziej „magicznych” aspektów TypeScripta, czyli jak zrobić non empty array oraz fixed size array. Droga do mistrzostwa w TS. Zaczynajmy.

Ok, najpierw przykład non empty array:

type NonEmptyArray<T> = [T, ...T[]];

const okay: NonEmptyArray<number> = [1, 2];
const alsoOkay: NonEmptyArray<number> = [1];
const err: NonEmptyArray<number> = []; // error!

Wydaje się proste, prawda? Ludzie narzekają na ten przykład. I w sumie rację mają:

type NonEmptyArr<T> = [T, ...T[]];

let anotherArray = [1,2,3];

let arr: NonEmptyArr<number> = [...anotherArray, 4];

//Type '[...number[], number]' is not assignable to type 'NonEmptyArr<number>'

Nawet ciekawe rozwiązanie podają:

type NonEmptyArr<T> = [T, ...T[]] | [...T[], T] | [T, ...T[], T];

let anotherArray = [1,2,3];

let arr: NonEmptyArr<number> = [...anotherArray, 4]; //ok

W sumie jest to jedyna opcja, aby móc robić takie spready, to nie zadziała:

type NonEmptyArr<T> = T[] & { 0: T }

let anotherArray = [1,2,3];

let arr: NonEmptyArr<number> = [...anotherArray, 4]; //err

Natomiast to nam się przyda do czego innego. Najpierw doprowadźmy to do porządku:

type NonEmptyArr<T> = T[] & { 0: T }

let arr: NonEmptyArr<number> = [1,2,3,4]; //ok

Ok, a można sobie dopisać length? W sensie kolejny &, length number? Zobaczmy:

type NonEmptyArr<T> = T[] & { 0: T } & {length: number};

let arr: NonEmptyArr<number> = [1,2,3,4]; //ok

Można nawet tak do tego podejść:

type NonEmptyArr<T> = { 0: T } & {[Idx: number]: T} & {length: number};

let arr: NonEmptyArr<number> = [1,2,3,4]; //ok

Ok, to teraz tak:

type FixedSizeArr<T, N extends number> = T[] & {length: N};

let arr: FixedSizeArr<number, 3> = [1,2,3]; //ok

let arr2: FixedSizeArr<number, 3> = [1,2,3,4]; //err

Natomiast żeby ta FixedSizeArr była spreadable to się nie da. Ja przynajmniej nie umiem.