Lambdaカクテル

京都在住Webエンジニアの日記です

Typescriptのオブジェクトから特定の型に適合するプロパティだけ抜き出した型をコンパイルタイミングで作る

オブジェクトからstring型のプロパティだけ取り出したいってことありませんか。僕はあります。何言ってんだという感じですがこういう感じです。

type EnumString = "foo" | "bar" | "piyo";

interface Mixed {
    a: string,
    b: number,
    c: object,
    d: EnumString
};

type NoneStringKeys<T> = {
    [P in keyof T]: T[P] extends string ? never : P;
}[keyof T];

type OnlyStringKeys<T> = {
    [P in keyof T]: T[P] extends string ? P : never;
}[keyof T];

type OmitString<T> = Pick<T, Extract<keyof T, NoneStringKeys<T>>>;
type OnlyString<T> = Pick<T, Extract<keyof T, OnlyStringKeys<T>>>;

const Y: OmitString<Mixed> = {
    b: 1,
    c: {}
}

const X: OnlyString<Mixed> = {
    a: "",
    d: "foo"
}

いったんKeyを生成して,その後でPickとExtractを使っています。かっこいいですね。これが型だけで、コンパイルタイミングでできるのすごいですね。

順を追って見ていくと、まずオブジェクト中にある特定の型のプロパティ名だけを取り出すような型コンストラクタを定義し、それを使って実際にプロパティをフィルタしています。

NoneStringKeysOnlyStringKeysがミソで、三項演算子が型にも適用できるのってすごいですよね。never型にすることで型を消すというテクニックがおもしろいと感じました。 というのも、neverとのunion型はnever型がちょうど消えてしまうんですね。

A | never => A

参考文献

github.com