typescript 在具有相同索引键的型别之间指派-“型别'...'无法指派给型别'never'”,

w6lpcovy  于 2022-11-18  发布在  TypeScript
关注(0)|答案(1)|浏览(147)

在我的应用程序中,我使用的值可以是静态值,也可以是返回这些值的函数。
对于typescript,我定义了静态值及其类型,如下所示

type Static = {
  key1: number;
  key2: string;
};

然后使用keyof Static

type DynamicValue<T> = () => T;
type Dynamic = {[key in keyof Static]: DynamicValue<Static[key]>};

在我的理解中,这应该和写这篇文章完全一样:

type DynamicValue<T> = () => T;
type Dynamic = {
  key1: DynamicValue<number>;
  key2: DynamicValue<string>;
};

(and实际上,在下面的代码中,这两种方法都会产生相同的错误)
现在我有了一个函数,它接受动态值并从中创建静态值,这就是问题所在:

function dynamicToStatic(d: Dynamic): Static {
  const o: Static = {key1: 0, key2: ''};
  
  for (const [key, callback] of Object.entries(d))
    // the left-hand side of this assignment is marked with error:
    //   "Type 'string | number' is not assignable to type 'never'."
    o[key as keyof Static] = callback();

  return o;
}

(该函数还有一个变体,它应该将Partial<Dynamic>作为参数,并返回Partial<Static>,它显示了类似的错误:“类型”字符串|number'无法指派给型别' undefined '。”)
它似乎与Object.keys无关,因为此版本具有相同的错误:

function dynamicToStatic(d: Dynamic): Static {
  const o: Static = {key1: 1, key2: ''};
  
  const keys = ['key1', 'key2'] as Array<keyof Static>;
  for (const key of keys)
    o[key] = d[key]();

  return o;
}

我知道我可以通过将有问题的行替换为

(o as any)[key as keyof Static] = callback();

但很明显,typescript认为我做的事情有问题,但我就是不知道什么地方可能有问题。
那么,为什么我在上面的代码中看到这个错误呢?错误消息中的类型“undefined”/“never”到底是什么?为什么?
(full代码)

k10s72fa

k10s72fa1#

由于o可以包含stringnumber的值,因此在此赋值中

o[key as keyof Static] = callback();

callback必须返回string & number才能正常运行(因为为了安全起见,你必须同时满足stringnumber)。否则,你可能会把一个字符串赋给一个数字,反之亦然!然而,string & number不可能的,这就是为什么它被简化为never(这就是never的来源)。因为你不能把string | number赋值给never,所以你会得到一个错误。
这个错误是 * 正确的 *,并且确实暗示了这个潜在的不安全赋值,但是我们知道它不是,因为键和回调应该有匹配的类型。有多种方法可以解决这个问题。
一种方法是强制转换为never,这看起来很奇怪,可能需要其他人花点时间来理解,但它确实有效,因为never可以赋值给never

o[key as keyof Static] = callback() as never;

我喜欢这个,因为它是最简单的......但您也可以使用Object.defineProperty(甚至Reflect.defineProperty):

Object.defineProperty(o, key, { value: callback() });

最后,另一种方法是赋值的helper函数:

function assign<T, K extends keyof T>(o: T, key: K, value: T[K]) {
  o[key] = value;
}

然后您可以将密钥转换为keyof Static

assign(o, key as keyof Static, callback());

相关问题