in keyof T

TypeScript 2.1 · TypeScript

この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でも補完されるし、もちろん違う名前を使ってるとコンパイルエラー。素敵。