🧶
Typescriptの型でいろいろ遊ぶ
型パズルで遊んでたところ、段々と面白くなってきたので、いろいろなものを実装してみます。
Replace
文字列を置き換えます。
type Replace<
S extends string,
P extends string,
R extends string
> = S extends `${infer F}${P}${infer L}` ? `${F}${R}${L}` : "";
let value: Replace<"string", "tri", "pri"> = 'spring';
頑張れば正規表現も実装できそうです。
Split
文字列を分割します
type Split<
S extends string,
Splitter extends string = "/"
> = S extends `${infer U}${Splitter}${infer U2}`
? [U, ...Split<U2>]
: S extends `${infer U}`
? [U]
: [];
type t = Split<"parent/child/item", "/">; // ["parent", "child", "item"]
このような感じで、infer と 再帰を利用するとかなり柔軟に書くことができます。
Join
文字列を結合します。
export type Join<T extends any[], S extends string> = T extends [
infer U,
...infer U2
]
? U extends string
? `${U}${S}${Join<U2, S>}`
: ""
: "";
type t = Join<["parent", "child", "item"], "/">; // parent/child/item
Trim
文字列から空白を削除します。
type Trim<T extends string> = T extends ' ' ? '': T extends `${infer L}${infer S}${infer R}` ? `${Trim<L>}${Trim<S>}${Trim<R>}` : T;
type t = Trim<' aaa '> // 'aaa'
Reverse
配列を逆転させます。
type Reverse<T extends any[]> = T extends [infer U, ...infer U2] ? [...Reverse<U2>, U] :[];
type t = Reverse<[0,1,2,3,4]>; // [4,3,2,1,0]
Tuple
油断するとすぐに any[]
みたいになりがちな型を与えられた配列から作成します。
type Tuple<T extends any[]> = T extends [infer U, ...infer U2]
? [U, ...Tuple<U2>]
: [];
type t = Tuple<[0, 1, 2, 3, '4']>; // [0,1,2,3,'4']
これだけだとただ返しているだけになってしまいますが、U
のところに好きな変換を挟むことで、自由なTupleを利用できます。
例えばこんな感じです。
type Tuple<T extends any[]> = T extends [infer U, ...infer U2]
? [{item: U}, ...Tuple<U2>]
: [];
type t = Tuple<[0, 1, 2, 3, '4']>; // [0,1,2,3,'4']
/*
type t = [{
item: 0;
}, {
item: 1;
}, {
item: 2;
}, {
item: 3;
}, {
item: "4";
}]
*/
readonly外し
type Mutable<T> = {
-readonly [K in keyof T]: Mutable<T[K]>;
};
const val = {'hoge':'fuga'} as const //{ readonly hoge: "fuga";}
type t = Mutable<typeof val>; // {'hoge':'fuga'}
as const
などで型にしたものを扱うときにreadonly
を外せて便利です。
配列を展開する
type Unpacked<T> = T extends (infer U)[] ? U : T;
type t : Unpacket<['a','b','c']>; // 'a'|'b'|'c';
与えられた配列を列挙する形に変換します。
Object.keysなどと組み合わせると便利です。
Lodashのgetみたいなやつ
指定したパスから値を取り出します。Split
を利用します。
type GetByKey<
S extends { [key: string]: any },
K extends any
> = K extends keyof S ? S[K] : never;
type Get<O extends { [key: string]: any }, P extends string[]> = P extends [
infer U
]
? GetByKey<O, U>
: P extends [infer U, ...infer U2]
? U2 extends string[]
? Get<GetByKey<O, U>, U2>
: never
: never;
type t = Get<
{ parent: { child: { item: "item" } } },
Split<"parent/child/item", "/">
>; // 'item'
そのほか
面白かったら追記していきます。
Discussion