TypeScript で、メソッドを含めない型を作る
TypeScript小ネタ。
TypeScript で、下記コードのような Interface からメソッドを抜いた型を作りたいとする。
type.ts
declare class Foo {
id: string
timeout: number
start: () => void
stop: () => void
on: (event: Event) => void
}
keyof T
から関数の型を持つキーを取っ払ったユニオン型を作り、それを使って Pick<T, K extends keyof T>
すればいける。
type.ts
type ExcludeMethods<T> = Pick<
T,
{
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K
}[keyof T]
>
やること
まず Mapped Types と Conditional Types を使って、関数の型であれば never 型、それ以外ならキーの型を作る。
type.ts
type ExcludeMethods<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K }
この時点では、キーとキー名の型しか出ない。
typescript:title=型
type ExcludeMethodsOfFoo = ExcludeMethods<Foo>
// {
// id: "id";
// timeout: "timeout";
// start: never;
// stop: never;
// on: never;
// }
次に Lookup Types で取り出す。ここではユニオン型が使えるので、[keyof T]
を指定する。
ユニオン型は never 型を省略するので、[keyof T]
からメソッドを持つキーが省略され、"id" | "timeout"
が取れる。
type.ts
type ExcludeMethods<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K }[keyof T]
type ExcludeMethodsOfFoo = ExcludeMethods<Foo>
// { id: "id" } "id" が入る
// { start: never } never なので省略される
// "id" | "timeout"
"id" | "timeout"
のユニオン型になったので、冒頭で書いたように Pick
で取れる。
最終的にはこう。
type.ts
type ExcludeMethods<T> = Pick<
T,
{
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K
}[keyof T]
>
const foo: ExcludeMethods<Foo> = {
id: 'foo',
// timeout: number が存在しないのでエラー
start: () => {}, // start は Pick されていないのでエラー
}