generated at
TypeScriptの配列型で基本的な配列操作関数を模倣する
TypeScriptのversionが上がるごとに、より簡潔に書けるようになっているので、もしかしたら古いかもしれないmrsekut


Head
ts
type Head<T extends unknown[]> = T extends [] ? never : T[0];
いろんな書き方がある
使用例.ts
type Head1 = Head<[]>; // never type Head2 = Head<[1]>; // 1 type Head3 = Head<[1, 2], 0>; // 1
never を返すのではなく、そもそも空配列を許容しないようにしても良い
ts
type Head<NonEmptyList extends [unknown, ...unknown[]]> = NonEmptyList[0];
使用例.ts
type Head1 = Head<[]>; // error type Head2 = Head<[1]>; // 1



Last
last :: [a] -> a
最後の要素を取り出す
inferを使って書く
ts
type Last<T extends unknown[]> = T extends [...unknown[], infer L] ? L : never;
lengthを使って書く
ts
type Last<T extends unknown[]> = [unknown, ...T][T['length']];
[unknown, ...T] とすることで、配列の要素を1つ増やしている部分がポイント
要素を増やさずにlengthすると、常にundefinedになってしまう
ts
type Last<T extends unknown[]> = T[T['length']]; type Last<T extends unknown[]> = T[T['length'] - 1]; // こう書きたい
TS v3.0の時点では複雑な書き方をする必要があった ref





Init
ts
type Init<T extends unknown[]> = T extends [...infer I, unknown] ? I : never;
使用例.ts
type Init1 = Init<[]>; // never type Init2 = Init<[1]>; // [] type Init3 = Init<[1, 2, 3]>; // [1,2]



Tail
ts







...A 型のPR
...A 型のproposal






↓全体的に書き方が古い。もっと簡潔に書ける


tail :: [a] -> [a] ref
head以外を返す
ts
type Tail<T extends any[]> = ((...x: T) => void) extends ( x: any, ...xs: infer XS ) => void ? XS : never;
変態的すぎるmrsekut
headと同じ用に書くと以下のようになりそうだがうまく行かない
ts
type Tail<T extends any[]> = T extends [any, ...(infer XS)[]] ? XS[] : never;
...(infer XS) こういうのが書けない
なので関数型 (x: any, ...xs: infer XS) => void を使うことで実現する
左辺の extends のあとの関数型 ( x: any, ...xs: infer XS) => void
( x: infer X, ...xs: infer XS) => void とかにしても良い
とはいえどのみち X は特に使わないのでanyのままでもいい
使用例.ts
type Tail1 = Tail<[]>; // [] type Tail2 = Tail<[1]>; // [] type Tail3 = Tail<[1, 2, 3]>; // [2,3]



cons :: a -> [a] -> [a]
リストの先頭に任意の型を1つ加える
ts
type Cons<Elm, T extends unknown[]> = [Elm, ...T]
使用例.ts
type Cons1 = Cons<1, [2,3]> // [1,2,3] type Cons2 = Cons<'a', [2,3]> // ['a',2,3]
このページでは関数型を使って書いているが、versionが上がってシンプルに書けるようになった
reverse :: [a] -> [a]
リストを反転する
ts
type Reverse<L extends any[], X extends any[] = []> = { 0: X; 1: Reverse<Tail<L>, Cons<Head<L>, X>>; }[L extends [] ? 0 : 1];
使用例.ts
type Reve1 = Reverse<[]>; // [] type Reve2 = Reverse<[1]>; // [1] type Reve3 = Reverse<[1, 2, 3]>; // [3,2,1]
merge :: [a] -> [a] -> [a]
ts
type Merge<A extends any[], B extends any[], R extends any[] = []> = { 0: Reverse<R>; 1: Merge<Tail<A>, B, Cons<Head<A>, R>>; 2: Merge<A, Tail<B>, Cons<Head<B>, R>>; }[A extends [] ? (B extends [] ? 0 : 2) : 1];
使用例.ts
type Merg1 = Merge<[], []>; // [] type Merg2 = Merge<[1, 2], []>; // [1,2] type Merg3 = Merge<[], [1, 2]>; // [1,2] type Merg4 = Merge<[1, 2], [3, 4, 5]>; // [1,2,3,4,5]
take :: number -> [a] -> [a]
ts
type Take<N extends number, T extends any[], R extends any[] = []> = { 0: Reverse<R>; 1: Take<N, Tail<T>, Cons<Head<T>, R>>; }[T extends [] ? 0 : R["length"] extends N ? 0 : 1];
使用例.ts
type Take1 = Take<2, []>; // [] type Take2 = Take<2, [1, 2, 3]>; // [1,2] type Take3 = Take<0, [1, 2, 3]>; // []
drop :: number -> [a] -> [a]
ts
export type Drop<N extends number, T extends any[], R extends any[] = []> = { 0: T; 1: Drop<N, Tail<T>, Cons<Head<T>, R>>; }[T extends [] ? 0 : R["length"] extends N ? 0 : 1];
使用例.ts
type Drop1 = Drop<0, []>; // [] type Drop2 = Drop<0, [1, 2, 3]>; // [1,2,3] type Drop3 = Drop<1, [1, 2, 3]>; // [2,3] type Drop4 = Drop<5, [1, 2, 3]>; // []
flat :: [[a]] -> [a]
ts
type Flat<T extends any[][], R1 extends any[] = [], R2 extends any[] = []> = { 0: Reverse<R2>; 1: Flat<Tail<T>, Head<T, []>, R2>; 2: Flat<T, Tail<R1>, Cons<Head<R1>, R2>>; }[T extends [] ? (R1 extends [] ? 0 : 2) : R1 extends [] ? 1 : 2];
使用例.ts
type Flat1 = Flat<[]>; //[] type Flat2 = Flat<[[]]>; //[] type Flat3 = Flat<[[1, 2], [], [3]]>; //[1,2,3]
replicate :: number -> a -> [a]
ts
export type Replicate<N extends number, T, R extends unknown[] = []> = { 0: R; 1: Replicate<N, T, Cons<T, R>>; }[R['length'] extends N ? 0 : 1];
使用例.ts
type Rep1 = Replicate<2, 3>; // [3, 3]; type Rep2 = Replicate<0, 2>; // []; type Rep3 = Replicate<3, number>; // [1, 2, 3]; type Rep4 = Replicate<5, string>; // ['1', '2', '3', '4', '5'];
atLeast :: number -> a -> [a]
replicateの「少なくともN個ある」版
ts
type AtLeast<N extends number, T> = AtLeastRec<N, T, T[], []>; type AtLeastRec<Num, Elm, T extends unknown[], C extends unknown[]> = { 0: T; 1: AtLeastRec<Num, Elm, Cons<Elm, T>, Cons<unknown, C>>; }[C extends { length: Num } ? 0 : 1];