Apr 9, 2022
7 mins read
今回もmattoさんのコードリーディングをしていく。
TypeScript Tip #3
今回はTypeScriptのユーティリティをまとめたライブラリts-toolbeltについて説明していく。
注意点としては、TypeScriptバージョンが4.1以上である必要がある。
数多くのユーティリティが提供されているため、今回はMattさんの解説に絞ってまとめていく。
ts-toolbelt
ライブラリの中から使用したいユーティリティをimportして使用する。
import { String, Union } from 'ts-toolbelt';
今回はURLの操作をする時に便利なユーティリティをまとめていく。
import { String, Union } from 'ts-toolbelt';
const query = '/home?a=foo&b=wow'
type Query = typeof query
//  type Query = "/home?a=foo&b=wow"
type SecondQueryPart = String.Split<Query, '?'>[1]
//  type SecondQueryPart = "a=foo&b=wow"
type QueryElements = String.Split<SecondQueryPart, '&'>
//  type QueryElements = ["a=foo", "b=wow"]
type QueryParams = {
    [ QueryElement in QueryElements[number] ]: {
        [ key in String.Split<QueryElement, '='>[0]]: String.Split<QueryElement, '='>[1]
    }
}[QueryElements[number]]
//  type QueryParams = {
//    a: "foo";
//  } | {
//    b: "wow";
//  }
const obj: Union.Merge<QueryParams> = {
    a: 'foo',
    b: 'wow'
}
//  const obj: {
//    a: "foo";
//    b: "wow";
//  }
type SecondQueryPart = String.Split<Query, "?">;でリテラル型を?で区切りタプル型を作成する。
よって、type SecondQueryPart = ["/home", "a=foo&b=wow"]になる。その後、ルックアップ型で要素を取得している。
type SecondQueryPart = String.Split<Query, '?'>[1]でtype SecondQueryPart = "a=foo&b=wow"の型を取得することができる。
TypeScript のルックアップ型と keyof キーワード
type QueryElements = String.Split<SecondQueryPart, "&">;は上記と同じように&で区切りタプル型を作成している。
そのため、type QueryElements = ["a=foo", "b=wow"]になる。
QueryParams型のインデックスシグネチャをみていく。
QueryElement in QueryElements[number]のQueryElements[number]は、以下の様になる。
QueryElements[number]
🔽
["a=foo", "b=wow"][number]
🔽
"a=foo" | "b=wow"
よって、QueryElement in QueryElements[number]はQueryElement in "a=foo" | "b=wow"でマップ型が作成される。
4. Key in String.Split<QueryElement, "=">[0]のString.Split<QueryElement, "=">[0]は以下になる。
a=fooの場合
String.Split<'a=foo', "=">[0]
🔽
"a"
上記と同じ様にString.Split<QueryElement,"=">[1]も時も以下の様になる。
String.Split<QueryElement,"=">[1]
🔽
"foo"
type QueryParams = {
    [QueryElement in QueryElements[number]]: {
        [Key in String.Split<QueryElement, "=">[0]]: String.Split<QueryElement,"=">[1];
    };
}
🔽
type QueryParams = {
    "a=foo": {
        a: "foo";
    };
    "b=wow": {
        b: "wow";
    };
}
type QueryParams = {
  [QueryElement in QueryElements[number]]: {
    [Key in String.Split<QueryElement, "=">[0]]: String.Split<QueryElement,"=">[1];
  };
}[QueryElements[number]];
🔽
type QueryParams = {
    "a=foo": {
        a: "foo";
    };
    "b=wow": {
        b: "wow";
    };
}["a=foo" | "b=wow"];
🔽
type QueryParams = {
    a: "foo";
} | {
    b: "wow";
}
Union.Merge<T>でユニオン型の型をインターセクション型に定義することができる。Union.Merge<QueryParams>
    
🔽
Union.Merge<
        {
            a: "foo";
        } | {
            b: "wow";
        }>
    
🔽
 {
     a: "foo";
     b: "wow";
 }
ts-toolbeltの存在を初めて知ったので、非常に勉強になった。比較的バージョンを新しくする必要があるので、 使用できる環境があれば、積極的に公式のドキュメントを参考にしていきたい。
TypeScript Tip #3
ts-toolbelt
TypeScript のルックアップ型と keyof キーワード