TypeScriptの配列型で基本的な配列操作関数を模倣する
TypeScriptのversionが上がるごとに、より簡潔に書けるようになっているので、もしかしたら古いかもしれないmrsekut.icon
Head
code:ts
type Head<T extends unknown[]> =
T extends [] ? never : T0; いろんな書き方がある
code:使用例.ts
type Head1 = Head<[]>; // never
type Head2 = Head<1>; // 1 type Head3 = Head<1, 2, 0>; // 1 neverを返すのではなく、そもそも空配列を許容しないようにしても良い
code:ts
type Head<NonEmptyList extends [unknown, ...unknown[]]> = NonEmptyList0; code:使用例.ts
type Head1 = Head<[]>; // error
type Head2 = Head<1>; // 1 Last
last :: [a] -> a
最後の要素を取り出す
inferを使って書く
code:ts
type Last<T extends unknown[]> =
T extends [...unknown[], infer L] ? L : never;
lengthを使って書く
code:ts
[unknown, ...T]とすることで、配列の要素を1つ増やしている部分がポイント
要素を増やさずにlengthすると、常にundefinedになってしまう
code:ts
type Last<T extends unknown[]> = T[T'length']; type Last<T extends unknown[]> = T[T'length' - 1]; // こう書きたい TS v3.0の時点では複雑な書き方をする必要があった ref Init
code:ts
type Init<T extends unknown[]> =
code:使用例.ts
type Init1 = Init<[]>; // never
type Init2 = Init<1>; // [] Tail
code:ts
...A型のPR
...A型のproposal
↓全体的に書き方が古い。もっと簡潔に書ける
head以外を返す
code:ts
type Tail<T extends any[]> = ((...x: T) => void) extends (
x: any,
...xs: infer XS
) => void ? XS : never;
変態的すぎるmrsekut.icon
headと同じ用に書くと以下のようになりそうだがうまく行かない
code: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のままでもいい
code:使用例.ts
type Tail1 = Tail<[]>; // []
type Tail2 = Tail<1>; // [] cons :: a -> [a] -> [a]
リストの先頭に任意の型を1つ加える
code:ts
type Cons<Elm, T extends unknown[]> = Elm, ...T code:使用例.ts
このページでは関数型を使って書いているが、versionが上がってシンプルに書けるようになった reverse :: [a] -> [a]
リストを反転する
code:ts
type Reverse<L extends any[], X extends any[] = []> = {
0: X;
1: Reverse<Tail<L>, Cons<Head<L>, X>>;
}[L extends [] ? 0 : 1];
code:使用例.ts
type Reve1 = Reverse<[]>; // []
type Reve2 = Reverse<1>; // 1 merge :: [a] -> [a] -> [a]
code: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];
code:使用例.ts
type Merg1 = Merge<[], []>; // []
type Merg2 = Merge<1, 2, []>; // 1,2 type Merg3 = Merge<[], 1, 2>; // 1,2 take :: number -> [a] -> [a]
code: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]; code:使用例.ts
type Take1 = Take<2, []>; // []
type Take3 = Take<0, 1, 2, 3>; // [] drop :: number -> [a] -> [a]
code: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]; code:使用例.ts
type Drop1 = Drop<0, []>; // []
type Drop4 = Drop<5, 1, 2, 3>; // [] flat :: [[a]] -> [a]
code: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];
code:使用例.ts
type Flat1 = Flat<[]>; //[]
type Flat2 = Flat<[[]]>; //[]
type Flat3 = Flat<1, 2], [], [3>; //1,2,3 replicate :: number -> a -> [a]
code:ts
export type Replicate<N extends number, T, R extends unknown[] = []> = {
0: R;
1: Replicate<N, T, Cons<T, R>>;
code:使用例.ts
type Rep1 = Replicate<2, 3>; // 3, 3; type Rep2 = Replicate<0, 2>; // [];
type Rep3 = Replicate<3, number>; // 1, 2, 3; atLeast :: number -> a -> [a]
replicateの「少なくともN個ある」版
code: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>>;