続 keyof T

in keyof T - まさたか日記

keyof Tを用いて連想配列でアクセスする際の縛りを前に書きましたが、もっとスマートな書き方を思いついたのでメモ。

test ('power of keyof', () => {
    function checkObject<T>(props: T): { [N in keyof T]?: string } {
        return Object.keys(props).reduce(
            (prior, key) => {
                // propsを型変換するのではなく、連想配列キーを「as keyof」でTへ縛る。
                const value = props[key as keyof T];
                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.');
});

変えたのは一箇所で、props[key as keyof T]のとこ。propsを「as { [key: string]: any }」で型変換するのではなく、連想配列キーを「as keyof」でTへ縛る。この応用で、関数の引数とかで、name: stringがあったときに、name: keyof Tというのも有用かと思います。

function foo<T>(obj: T, name: keyof T): any {
    return T[name];
}

コードとしてはあまり意味がないけど、これでしょうがなくanyで受けてたところが改善されます。