通用键约束:必须是特定类型对象的键

时间:2017-02-12 19:41:02

标签: typescript

我不确定这是可能的,但我非常接近。

如果我有这个物体/形状:

export const initialState: State = {
    foods: {
        filter: '',
        someForm: {
            name: '',
            age: 2,
        },
        someFormWithoutAnAge: {
            name: '',
        },
    }
};

declare function pickAForm<R extends keyof State, S extends keyof State[R]>(key1: R, key2: S): void;

该功能效果很好,我可以使用类型安全调用pickAForm("foods", "someForm"),如果我pickAForm("foods", "somePropertyThatDoesntExist")

,则会出错

但是,我想增加额外的安全性,因为你只能挑选具有特定形状的物品。示例:someForm应该有效但someFormWithoutAnAge应该失败,因为您选择必须具有年龄属性。像这样:

declare function pickAFormWithAge<R extends keyof State, S extends keyof State[R], T extends State[R][S] & {age: number}>(key1: R, key2: S): void;

但我完全不确定如何去做。总结一下:

pickAFormWithAge('foods', 'someForm') // Passes
pickAFormWithAge('foods', 'someFormWithoutAge') // Should fail, does not look like {age: number}
pickAFormWithAge('foods', 'blah') // Should fail, not a key

1 个答案:

答案 0 :(得分:2)

我能做到这一点的唯一方法是:

一个。约束数据结构以匹配字符串文字,而不是相反 湾将数据结构作为函数参数传递。

const state = {
    foods: {
        filter: '',
        someForm: {
            name: 'Some form',
            age: 2
        },
        someFormWithoutAnAge: {
            name: 'other form',
            priority: 10
        }
    }
};

interface HasAge { age: number }

// IMPLEMENTATION
function getForm<O extends {[P in P1]: {[P in P2]: HasAge}}, P1 extends string, P2 extends string>(o: O, p1: P1, p2: P2) {
    return (o[p1] as any)[p2] as any;
}

// USAGE
const form1 = getForm(state, 'foods', 'someForm'); // GOOD
const form2 = getForm(state, 'foods', 'someFormWithoutAnAge'); // ERROR
const form3 = getForm(state, 'foods', 'blah'); // ERROR

更简单,更灵活的解决方案是使用常规代码。 pickAForm采用函数而不是字符串文字。

const form1 = pickAForm((state) => state.foods.someForm);
// ...or...
const form1 = pickAForm(() => initialState.foods.someForm);