考虑下一个例子:
type Result = {
prop1?: string | undefined;
prop2?: number | undefined;
} | {
prop3?: string | undefined;
}
type Keys = keyof Result // never
上述联合中没有公共键,因此 TS 无法确定它应该返回给您哪些键。这就是您收到错误消息的原因。
要处理这种联合,您可以使用@Shivam Singla 的方式或这种通用方式:
// credits goes to Titian Cernicova-Dragomir
//https://stackoverflow.com/questions/65805600/type-union-not-checking-for-excess-properties#answer-65805753
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
所以,不要写:
keyof Groups[keyof Groups];
你可以写:
keyof StrictUnion<Groups[keyof Groups]>
这是我的完整解决方案:
export type Groups = {
group1: {
prop1?: string;
prop2?: number;
};
group2: {
prop3?: string;
};
};
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
interface AllGroups {
groupName: keyof Groups;
prop: keyof StrictUnion<Groups[keyof Groups]>;
values: string[];
};
const allGroups = <T extends AllGroups, U extends {
0: ReadonlyArray<T>,
}[
T['groupName'] extends keyof Groups
? T['prop'] extends keyof Groups[T['groupName']]
? 0 : never : never]>(arr: ReadonlyArray<T> & U) => arr;
const result = allGroups(
[{
groupName: 'group1',
prop: 'prop2',
values: ['a']
},
{
groupName: 'group1',
prop: 'prop1',
values: ['a']
}]) // ok
const result2 = allGroups(
[{
groupName: 'group1',
prop: 'prop2',
values: ['a']
},
{
groupName: 'group1',
prop: 'prop3', // expected error
values: ['a']
}]) // ok
Playground
缺点:在这里你应该使用除了类型推断之外什么都不做的函数。
我假设它会被 V8 引擎优化。
您也可以使用closure webpack plugin 来摆脱函数调用。
但是,我投票支持 @Shivam Singla 的解决方案,因为没有函数开销。
我刚刚发布了这个解决方案给你一些替代方法。
这个类型有点复杂:
const allGroups = <T extends AllGroups, U extends {
0: ReadonlyArray<T>,
}[
T['groupName'] extends keyof Groups
? T['prop'] extends keyof Groups[T['groupName']]
? 0 : never : never]>(arr: ReadonlyArray<T> & U) => arr;
但这就是我验证函数参数的方式。
如果您对解释感兴趣,请联系我,我会更新答案