TypeScriptの配列型で基本的な配列操作関数を模倣する
TypeScriptのversionが上がるごとに、より簡潔に書けるようになっているので、もしかしたら古いかもしれないmrsekut.icon
Head
code:ts
type Head<T extends unknown[]> =
T extends [] ? never : T0;
いろんな書き方がある
ref TypeScriptのあるある部分型の型制約と条件分岐#61c83c311982700000150b5d
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
type-challenges 14
Last
last :: [a] -> a
最後の要素を取り出す
inferを使って書く
code:ts
type Last<T extends unknown[]> =
T extends [...unknown[], infer L] ? L : never;
lengthを使って書く
code:ts
type Last<T extends unknown[]> = unknown, ...T[T'length'];
[unknown, ...T]とすることで、配列の要素を1つ増やしている部分がポイント
要素を増やさずにlengthすると、常にundefinedになってしまう
code:ts
type Last<T extends unknown[]> = T[T'length'];
type Last<T extends unknown[]> = T[T'length' - 1]; // こう書きたい
type-challenges 15
TS v3.0の時点では複雑な書き方をする必要があった ref
Init
code:ts
type Init<T extends unknown[]> =
T extends ...infer I, unknown ? I : never;
code:使用例.ts
type Init1 = Init<[]>; // never
type Init2 = Init<1>; // []
type Init3 = Init<1, 2, 3>; // 1,2
type-challenges 16
Tail
code:ts
#WIP
https://qiita.com/uhyo/items/80ce7c00f413c1d1be56
https://docs.google.com/presentation/d/1-ZKRVh00WZpci9mM3UmiM2zjIJSJi1SDbYgG4YX8e7I/edit#slide=id.p
TypeScript 3.0のExtracting and spreading parameter lists with tuplesで遊ぼう - Qiita
Tuples in rest parameters and spread expressions by ahejlsberg · Pull Request #24897 · microsoft/TypeScript
...A型のPR
Proposal: Variadic Kinds -- Give specific types to variadic functions · Issue #5453 · microsoft/TypeScript
...A型のproposal
https://qiita.com/uhyo/items/e9a03aa4ea2db8d1d7fe
https://github.com/Microsoft/TypeScript/pull/24897
https://qiita.com/uhyo/items/80ce7c00f413c1d1be56
https://qiita.com/kazatsuyu/items/7b5e8366601c65a2212a
↓全体的に書き方が古い。もっと簡潔に書ける
tail :: [a] -> [a] ref
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>; // []
type Tail3 = Tail<1, 2, 3>; // 2,3
cons :: a -> [a] -> [a]
リストの先頭に任意の型を1つ加える
code:ts
type Cons<Elm, T extends unknown[]> = Elm, ...T
code:使用例.ts
type Cons1 = Cons<1, 2,3> // 1,2,3
type Cons2 = Cons<'a', 2,3> // 'a',2,3
このページでは関数型を使って書いているが、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
type Reve3 = Reverse<1, 2, 3>; // 3,2,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
type Merg4 = Merge<1, 2, 3, 4, 5>; // 1,2,3,4,5
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 Take2 = Take<2, 1, 2, 3>; // 1,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 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]
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>>;
}[R'length' extends N ? 0 : 1];
code:使用例.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個ある」版
https://qiita.com/uhyo/items/80ce7c00f413c1d1be56
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>>;
}C extends { length: Num } ? 0 : 1;