typescript - 交集 'xxx & xxx' 减少为 'never',因为属性 'xxx' 在某些成分中存在类型冲突

标签 typescript

我在使用 ts 泛型时遇到错误,这里是简单的代码:

Typescript Playground

在最后一行,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">;

所以taskTask<"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)不安全,并且 taskotherTask 具有相同的联合类型,然后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

因此,这是处理此类相关联合类型的推荐方法。

Playground link to code

关于typescript - 交集 'xxx & xxx' 减少为 'never',因为属性 'xxx' 在某些成分中存在类型冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72321759/

相关文章:

html - 使用 angular2 在 html 中调用函数的最佳实践是什么?

angular - TypeScript: '(error: any) => void' 类型的参数不可分配给类型的参数

javascript - 如何在 vue.js 中导入 ipcRenderer? __dirname 未定义

javascript - NestJS无法解析服务mongodb的依赖关系

javascript - 无法使用 Typescript 从表中删除一行

reactjs - 汇总保留模块根未按预期工作

angular - 如何在 angular2 和 ionic2 中同时保留组件的 DOM 元素和模板字符串

javascript - 在 TypeScript 和 JavaScript 中使用 ESLint 和 Prettier

typescript - 如何检测 Visual Studio 代码扩展中的复制和剪切操作?

javascript - Angular 路由/导航无法正常工作 : Appending instead of replacing component