🧶

Typescriptの型でいろいろ遊ぶ

2022/02/17に公開

型パズルで遊んでたところ、段々と面白くなってきたので、いろいろなものを実装してみます。

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