【问题标题】:Typescript compiler never Error: Type 'string | number' is not assignable to type 'never'. Type 'string' is not assignable to type 'never'Typescript 编译器从不出错:Type 'string | number' 不可分配给类型 'never'。类型“字符串”不可分配给类型“从不”
【发布时间】:2020-01-31 09:08:40
【问题描述】:

这是我得到的一段代码:

interface Obj {
  a: number
  b: string
}

const obj: Obj = {
  a: 1,
  b: 'hi'
}

function fn(key: keyof Obj, value: Obj[keyof Obj]) {
  let foo = obj[key]
  obj[key] = value
}

fn("a", 2)

所以我想做的是,我希望函数fn() 能够更新对象obj 属性,该函数的第一个参数是obj 具有的任何键(在Obj 中定义接口),第二个参数是你要赋予该键的值。

但是,在obj[key] = value这一行出现错误的typescript弹窗,即:

Type 'string | number' is not assignable to type 'never'.
  Type 'string' is not assignable to type 'never'.(2322)

这是截图:

这里发生了一件奇怪的事情,如果你将鼠标悬停在变量foo(即图片中的第13行)上,它会说:

let foo: string | number

这意味着,obj[key] 的类型是 string | number,但错误提示 obj[key] 类型是 never

所以我的第一个问题是:string | number 类型为什么会神奇地变成 never 类型?有没有办法解决这个问题?

然后我得到了另一个解决这个问题的代码:

interface Obj {
  a: number
  b: string
}

const obj: Obj = {
  a: 1,
  b: 'hi'
}

function fn<K extends keyof Obj>(key: K, value: Obj[K]) {
  obj[key] = value
}

fn("a", 2)

因此我的第二个问题是:为什么使用Generics 来解决问题?这里的关键字extends 是什么?

顺便说一句,所有代码都在 typescript 3.7.5 版本中测试。

我不是以英语为母语的人,希望我能清楚地解释我的困惑。

【问题讨论】:

    标签: typescript generics types


    【解决方案1】:

    错误是什么,这是因为keyof Obj 可能是"a""b",它们的类型为numberstring。在表达式obj[key] 中,编译器不知道属性类型,它也可能是numberstring,所以它不允许这样的赋值。 Here 是同一个问题。您可以找到解释 here,请参阅修复对索引访问类型的不正确写入

    在泛型函数K extends keyof Obj 的情况下,K 类型也可以是"a""b",但是当您调用函数fn("a", 2) 时,您将隐式设置K"a",编译器从第一个参数推断K 类型。所以现在,在调用上下文中key"a" 类型并且Obj[K]number,因此分配变得正确。


    我只是试图向我不是程序员的妻子解释其中的区别 :) 我认为这也可能会有所帮助:

    通常的功能: 假设你正在吃蛋糕,但你的眼睛是闭着的。你知道它可能是樱桃蛋糕或香蕉蛋糕。你喜欢它的味道,但你不能说“多么美味的香蕉蛋糕!”因为你不确定它是不是香蕉蛋糕。

    通用函数: 在这种情况下,你的眼睛是睁开的,你可以选择你想吃的蛋糕,但你仍然有两个选择:樱桃或香蕉。现在,如果您选择了香蕉蛋糕并品尝了它,您可以说“香蕉蛋糕真好吃!”。

    【讨论】:

    • 感谢您的解释,但我只收到了一个愚蠢的问题,正如您所说:“您将 K 隐式设置为 a”,但为什么 keyof Obj 不能这样做?为什么也不能将keyof Obj 隐式设置为'a'?引擎盖下到底发生了什么?
    • @Limboer 泛型允许您在调用函数时指定类型。通常的函数参数类型是常量,例如 keyof Obj 在您的情况下。对于泛型函数,您告诉编译器该函数接受K 类型的参数,并且该类型应满足约束extends keyof Obj,换句话说,它必须是"a""b"。当您调用函数时,您明确指定类型,如fn&lt;"a"&gt;("a", 2),或者可以从传递给函数的参数隐式推断,但现在它不是联合类型"a" | "b",而只是"a"。希望对你有帮助
    • @Limboer 我还为答案添加了一个现实生活中的解释,我希望它也可能有所帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 2022-12-06
    • 2022-08-14
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 2021-01-12
    相关资源
    最近更新 更多