TypeScriptのinterfaceやtypeで、関数やオブジェクトの型を定義する際、どんな書き方をすればいいかちょこちょこ迷うのでメモ。
関数
numberを引数に取り、stringを返す関数の場合、以下のようになる。
// interfaceの場合 interface ExampleFunctionInterface { (value: number): string } // typeの場合 type ExampleFunctionType = (value: number) => string
旧ハンドブックにはinterfaceでの定義方法が記載されていたが、新しいハンドブックからはtypeでの定義方法しか見つけられなかった。
オブジェクト
インデックスシグネチャの型を指定してやればいい。
キーの型にはstring、number、Symbolが使える。
// interfaceの場合 interface StringKeyObjectInterface { [key: string]: unknown } // typeの場合 type StringKeyObjectType = { [key: string]: unknown }
また、ユーティリティータイプの Record<Keys, Type>
も使える。こちらのほうが可読性が高いと思う。
TypeScript: Documentation - Utility Types
type StringKeyRecord = Record<string, unknown>
ちなみに、 {}
は ban-types | TypeScript ESLint によると、任意のnullではないものを表すらしい。
Don't use `{}` as a type. `{}` actually means "any non-nullish value". - If you want a type meaning "any object", you probably want `Record<string, unknown>` instead. - If you want a type meaning "any value", you probably want `unknown` instead. - If you want a type meaning "empty object", you probably want `Record<string, never>` instead.
Union型をキーとしたオブジェクト
ハンドブックの例にもあるが、stringやnumberからなるUnion型をRecordのキーに指定すると、キーに指定した値が過不足がある場合はコンパイルエラーにしてくれる。
type MyUnion = 'one' | 'two' | 'three' const myUnionRecord: Record<MyUnion, number> = { one: 1, two: 2, three: 3, }
インデックスシグネチャでも同様の指定ができるが、Mapped typeにする必要がある。
type MyUnion = 'one' | 'two' | 'three' const myUnionIndexSignature: { [key in MyUnion]: number } = { one: 1, two: 2, three: 3, }
複数のUnion型をキーにする場合、それぞれの型をUnion型にしてやればいい。
type MyUnion1 = 'one' | 'two' | 'three' type MyUnion2 = 'four' | 'five' | 'six' const myUnionRecord: Record<MyUnion1 | MyUnion2, number> = { one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, } const myUnionIndexSignature: { [key in MyUnion1 | MyUnion2]: number } = { one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, }
キーの不足を許す場合、キーをオプショナルにすればいいが、Recordではキーが string | number | symbol
である必要があり、undefinedを含むことができないため、宣言できない模様(方法があるかもしれないが、見つけられなかった)。
インデックスシグネチャの場合は、Mapped typeに ?
を付けてやればいい。
type MyUnion = 'one' | 'two' | 'three' // 以下はコンパイルエラー // type MyUnionRecord = Record<MyUnion | undefined, number> // こちらは問題なし const myUnionIndexSignature: { [key in MyUnion]?: number } = { two: 2, }
ただし、Union型自体にundefinedが含まれる場合、Recordと同様の理由でコンパイルエラーとなる模様。
type MyUnion = 'one' | 'two' | 'three' | undefined // 以下はコンパイルエラー // const myUnionIndexSignature: { [key in MyUnion]?: number } = ...
振り返り
interfaceよりtypeを使うことが多いからか、typeでの定義例は多いがinterfaceの例が少ない気がしたのでメモしておく。
また、今回参照したTypeScript ハンドブックには、他にコンストラクタの定義の方法なども記載されており、いい勉強になった。