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

Typescript 通用键约束:必须是特定对象类型的键,typescript,Typescript,我不确定这是否可能,但我已经很接近了 如果我有此对象/形状: export const initialState: State = { foods: { filter: '', someForm: { name: '', age: 2, }, someFormWithoutAnAge: { name: '', }, } }; d

我不确定这是否可能,但我已经很接近了

如果我有此对象/形状:

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;
但我一点也不知道该怎么做。总结如下:

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

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

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

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

如果您将
State
的定义添加到您的问题中,这会有所帮助。State看起来就像initialState一样。所以想一想,你说有效的部分在操场上不起作用,它会产生这样的错误:
类型为“someForm”的参数不能赋值给类型为“never”的参数
Odd,它在VS代码中工作得很好,在webpack中编译得很好。也许操场使用的是旧版本?但是它在正则类型脚本中工作。尝试将
S
参数指定为
S扩展键of(State[R]&{age:number;})
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
const form1 = pickAForm((state) => state.foods.someForm);
// ...or...
const form1 = pickAForm(() => initialState.foods.someForm);