在打字稿中,我知道,为了类型安全,当你有函数的联合时,如果你想调用这个函数,你必须向它传递它的参数的交集而不是联合,但是当你已经有函数时,这种行为可能会很烦人类型检查可确保无论您要调用联合体的哪个函数,即使传递联合体而不是传递所有可能的参数,您也始终会向其传递正确的参数。让我用一个小例子来解释一下:
type Runner = {
a: () => void,
b: (param: { x: number }) => void,
c: (param: { y: string, z: number }) => string,
};
type Action = {
[K in keyof Runner]: { kind: K, param: Runner[K] extends () => any ? unknown : Parameters<Runner[K]>[0] }
}[keyof Runner];
const runner: Runner = {
a: () => {},
b: ({ x }) => {},
c: ({ y, z }) => "c",
};
const action = { kind: "c", param: { y: "5", z: 5 } } as Action;
runner[action.kind](action.param);
在这里我们可以看到
param
字段与 kind
字段相关联,即 Runner
的每个字段,因此联合的每个可能的函数,所以我们知道我们将根据 拥有正确的参数kind
字段,但打字稿仍然会抱怨,因为你无法使用参数的并集来调用函数的并集。是否可以很好地处理此类情况,而无需添加 any
演员阵容?
我认为你需要停止使用 const 断言并让 TypeScript 变得智能。
type Runner = {
a: () => void,
b: (param: { x: number }) => void,
c: (param: { y: string, z: number }) => string,
};
type Action = {
[K in keyof Runner]: { kind: K, param: Runner[K] extends () => any ? unknown : Parameters<Runner[K]>[0] }
}[keyof Runner];
const runner: Runner = {
a: () => {},
b: ({ x }) => {},
c: ({ y, z }) => "c",
};
const action : Action = { kind: "c", param: { y: "5", z: 5 } };
const action2 : Action = { kind: "b", param: { x : 2} };
runner[action.kind](action.param);
runner[action2.kind](action2.param);
Typescript 将操作的两个字段视为并集的所有可能值,直到您给它一些东西来区分它并缩小类型。
const action = { kind: "c", param: { y: "5", z: 5 } } as Action;
runner[action.kind](action.param); // error
if (action.kind === "c") {
runner[action.kind](action.param); // OK, action is narrowed to one member of the union
}
const action2:Action = { kind: "c", param: { y: "5", z: 5 } };
runner[action2.kind](action2.param); // OK, action2 is narrowed to one member of the union
这在未来可能会得到改善,因为他们不断完善控制流分析,但现在,您必须为 TS 提供一种区分联合的方法或对其进行强制转换。我尝试重组我的代码以避免强制转换,因为在 TS 中强制转换只不过是关闭编译器,但如果必须的话,我会在隔离函数中执行此操作,然后广泛地使用单元测试来覆盖它。