我在使用 ts 泛型时遇到错误,这里是简单的代码:
在最后一行,ts 报告以下错误:
error TS2345: Argument of type 'Task<"build"> | Task<"repair">' is not assignable to parameter of type 'never'.
The intersection 'Task<"build"> & Task<"repair">' was reduced to 'never' because property 'type' has conflicting types in some constituents.
44 action_map[task.type].execute(task);
我尝试使用 switch 来避免错误:
function execute<T extends TaskType>(task: Task<T>) {
switch (task.type) {
case "build":
// now type of `task` should be `Task<"build">`
BuildAction.execute(task);
break;
case "repair":
// now type of `task` should be `Task<"repair">`
RepairAction.execute(task);
break;
default:
// now type of `task` should be `Task<never>`
console.log("Error");
}
}
但更糟糕的是:
error TS2345: Argument of type 'Task<T>' is not assignable to parameter of type 'Task<"build">'.
Type 'T' is not assignable to type '"build"'.
Type 'keyof TMap' is not assignable to type '"build"'.
Type '"repair"' is not assignable to type '"build"'.
50 BuildAction.execute(task);
我注意到 vscode 的类型提示为 task
总是Task<T>
而不是我所期望的。
那么,我该怎么办?
最佳答案
您面临的问题是编译器对于处理相关联合类型没有很好的直接支持,如 microsoft/TypeScript#30581 中所述。 .
您的task
属于 union type :
declare var task: Task<"build"> | Task<"repair">;
所以task
是 Task<"build">
或Task<"repair">
。和action_map[task.type].execute
因此也是联合类型:
const execute = action_map[task.type].execute;
// const execute: ((task: Task<"build">) => void) | ((task: Task<"repair">) => void)
这意味着execute
将接受 Task<"build">
或者它会接受 Task<"repair">
,但不会同时接受两者。假设我们还有另一项任务:
declare var otherTask: Task<"build"> | Task<"repair">;
显然使用 execute
是不安全的执行该任务:
execute(otherTask); // error!
毕竟,otherTask
可能是Task<"build">
而execute
可能是((task: Task<"repair">) => void)
。不幸的是,编译器可以使用这些类型来确定某些东西是否安全。自 execute(otherTask)
不安全,并且 task
与 otherTask
具有相同的联合类型,然后execute(task)
由于同样的原因被视为不安全:
execute(task); // same error!
当然我们知道execute
将接受task
因为它们彼此相关。但那是因为我们正在跟踪 task
的身份变量而不仅仅是类型。编译器无法区分 execute(otherTask)
之间的区别和execute(task)
。因此它担心它们可能不相关。
错误提到交集的原因是因为调用函数并集的唯一安全方法是 call it with an intersection of the parameters 。如果execute
接受Task<"build">
或Task<"repair">
但我们不知道是哪一个,唯一安全的通过方式是两者都是 Task<"build">
和 Task<"repair">
...因此 Task<"build"> & Task<"repair">
。当然,这样的事情是不可能的。它们具有冲突的属性,因此您会收到有关 the never
type 的消息.
这就是您看到错误的原因。
至于修复它,TypeScript 4.6 引入了一些 improvements特别是为了解决 microsoft/TypeScript#30581;您可以在 microsoft/TypeScript#47109 中阅读相关内容.
推荐的方法是制作 generic功能。如果K extends TaskType
是泛型类型参数,编译器将允许您调用 (task: Task<K>)=>void
类型的函数参数类型为Task<K>
。 TypeScript 4.6 中的改进意味着编译器将看到 action_map[task.type]
作为 (task: Task<K>)=>void
类型:
type SomeTask<T extends TaskType = TaskType> = { [K in T]: Task<K> }[T];
function genericExecute<K extends TaskType>(task: SomeTask<K>) {
action_map[task.type].execute(task); // okay
}
SomeTask<T>
type 不是编译器所必需的,但它展示了如何轻松获得任务类型的联合。如果TaskType
包含更多工会成员,SomeTask
也会获得这些成员。还有genericExecute()
的尸体被认为是安全的。
幸运的是,您可以调用genericExecute(task)
没有问题:
genericExecute(task); // okay
因此,这是处理此类相关联合类型的推荐方法。
关于typescript - 交集 'xxx & xxx' 减少为 'never',因为属性 'xxx' 在某些成分中存在类型冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72321759/