in keyof T
このTypeScript2.1で追加されたという keyof演算子をうまくつかうと、オブジェクトのプロパティを縛るのに有用でした。
test ('power of keyof', () => { // 返値のin keyofという受け方が今回の話題。今回Nは捨て型だけど、使ってT[N]とか可能。 function checkObject<T>(props: T): { [N in keyof T]?: string } { return Object.keys(props).reduce( (prior, key) => { // 値をとるためにstringのキー&anyの値セットを持つものとしてasで受ける const value = (props as { [key: string]: any })[key]; if (typeof value === 'number' && value > 0) { return prior; } return { ...prior, [key]: `${key} is invalid.` }; }, {}, ); } type Target = { first: string, second: number, third: number, fourth: boolean, }; const target: Target = { first: '', second: 10, third: -3, fourth: true }; const result = checkObject<Target>(target); expect(result.first).toBe('first is invalid.'); // <- この"first"でIDEの補完が効く! expect(result.second).toBeUndefined(); // 以下、resultのプロパティ名がTargetのそれと同じ // result.fifth; // <- これはコンパイルエラー!素敵。 });
props: Tに対して、props[key]と直接できないのは、便宜上 (props as { [key: string]: any })[key]とas演算で逃げておいて、その後の戻値については、 { [N in keyof T]?: string }なので、プロパティ名は関数呼び出しの時に決めたTのプロパティ名と同じになり、IDEでも補完されるし、もちろん違う名前を使ってるとコンパイルエラー。素敵。