{...} 类型的参数不可分配给“从不"类型的参数 [英] Argument of type {...} is not assignable to parameter of type 'never'

查看:39
本文介绍了{...} 类型的参数不可分配给“从不"类型的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试基于另一个具有相同键的类型创建一个类型,其值的类型基于但不等于另一种类型.它类似于 ReactsetState,它接受一个值或一个获取当前值并返回一个新值的函数.

I'm trying to create a type based on another one with the same keys and whose value's type is based on, but not equal to, the another type. It's similar to React's setState, where it accepts either a value or a function that get the current value and returns a new one.

经过研究,与此错误相关的所有问题似乎都与缺少类型提示有关.我已经尝试为所有 nextcurrentnewTheme[key] 变量指定类型.它没有用.

After research, all issues related to this error seems related to a lack of type hinting. I already tried to specified the types for all next, current and newTheme[key] variables. It didn't work.

type Mode = "light" | "dark";

interface Theme {
  mode: Mode;
  test: number;
}

type ThemeUpdate = {
  [K in keyof Theme]: Theme[K] | ((current: Theme[K]) => Theme[K])
};

const reducer = (currentTheme: Theme, action: Partial<ThemeUpdate>): Theme => {
  const newTheme: Partial<Theme> = {};

  (Object.keys(action) as (keyof Theme)[]).forEach((key: keyof Theme) => {
    const next = action[key];
    const current = currentTheme[key];
    newTheme[key] = typeof next === "function" ? next(current) : next;
    //                                                ^^^^^^^
    // Argument of type 'number | "light" | "dark"' is not assignable to parameter of type 'never'. Type 'number' is not assignable to type 'never'
  });

  return { ...currentTheme, ...newTheme };
};

我期望的是 next 函数将根据当前键解析参数类型.相反,参数被解析为 never 类型,我收到错误消息:

What I expect is that the next function will resolve the argument type according to the current key. Instead, the argument get resolved to the type never and I get the error message:

'number 类型的参数 |光" |"dark"' 不可分配给从不"类型的参数.类型数字"不可分配给类型'从不'`

Argument of type 'number | "light" | "dark"' is not assignable to parameter of type 'never'. Type 'number' is not assignable to type 'never'`

next 函数应该在 key === "mode" 时将 current 参数推断为 Mode,当 key === "test" 应该被推断为 number

The next function should infers the current argument as Mode when key === "mode", and when key === "test" it should be inferred as number

推荐答案

是的,这是我一直在调用的 TypeScript 痛点之一 相关类型.问题是您有一组联合类型的值,它们彼此不独立.nextcurrent 的关联方式取决于 key 的值.但是 TypeScript 只是将 next 视为 values-or-functions-or-undefined(这是正确的)的并集,将 current 视为值的并集(这也是正确的)没有意识到next不可能对应"mode"current不可能对应"test" 同时.改进了对调用函数的联合 只会让这个问题更加混乱,因为 never 交集并没有提供太多关于发生了什么的线索.

Yeah, this is one of those pain points in TypeScript that I've been calling correlated types. The problem is that you have a set of values of union types which are not independent of each other. next is correlated to current in a way that depends on the value of key. But TypeScript just sees next as a union of values-or-functions-or-undefined (which is correct) and current as a union of values (which is also correct) without realizing that it is impossible for next to correspond to "mode" and for current to correspond to "test" at the same time. The improved support for calling unions of functions only makes this problem more confusing, since the never intersection doesn't give much clue as to what's going on.

这里没有很好的解决方案.我发现处理这个问题的方法是手动引导编译器完成不同的情况,如:

There's no great solution here. The ways to deal with this I've found are either to walk the compiler manually through the different cases, as in:

  (Object.keys(action) as (keyof Theme)[]).forEach(key => {
    switch (key) {
      case "mode": {
        const next = action[key];
        const current = currentTheme[key];
        newTheme[key] = typeof next === "function" ? next(current) : next; // okay
        break;
      }
      case "test": {
        const next = action[key];
        const current = currentTheme[key];
        newTheme[key] = typeof next === "function" ? next(current) : next; // okay
        break;
      }
    }
  });

类型安全但很乏味...

which is quite type safe but is tedious...

或者,否则您将需要在某处进行类型断言以说服编译器您所做的事情是安全的(这是承担安全责任;编译器正在放弃).对于这个特定问题,我会考虑使 forEach() 回调成为键类型 K 中的通用函数,然后使用您的断言告诉编译器 next 以它理解的方式依赖于 K:

Or, else you will need to make a type assertion somewhere to convince the compiler that what you are doing is safe (which is you taking on the responsibility for the safety; the compiler is giving up). For this particular issue, I'd consider making the forEach() callback a generic function in the key type K and then using your assertion to tell the compiler that next is dependent on K in a way it understands:

  (Object.keys(action) as (keyof Theme)[]).forEach(
    // generic
    <K extends keyof Theme>(key: K) => {
      const next = action[key] as  // assert here
        | Theme[K]
        | ((current: Theme[K]) => Theme[K])
        | undefined;
      const current = currentTheme[key];
      newTheme[key] = typeof next === "function" ? next(current) : next; // okay
    }
  );

这更方便但不那么安全.

which is more convenient but not as safe.

我一般建议这里的断言(链接代码) 并希望有一天能在 TypeScript 中引入对相关类型的更好支持.

I generally recommend assertions here (link to code) and hope that one day better support for correlated types in TypeScript is introduced.

好的,希望有帮助.祝你好运!

Okay, hope that helps. Good luck!

这篇关于{...} 类型的参数不可分配给“从不"类型的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆