TypeScript小状况之对象属性赋值

6,569 阅读2分钟

前言

最近开始在复杂项目中使用TypeScript,然而在使用过程中遇到了一些状况和报错,决定开一个系列记录遇到的问题和我的解决方法。

对象属性赋值

在TypeScript里面,当需要给对象属性赋值的时候,经常就会遇到下图所示的错误提示。

interface Ifoo{
  name: string;
  age: number;
  weight: number;
  isEngineer: boolean;
}

function setProp (foo: Ifoo, key: keyof Ifoo, val: any) {
  foo[key] = val; // Type 'any' is not assignable to type 'never'.
}

typescript错误提示

错误提示信息:

Type 'any' is not assignable to type 'never'.

因为我们的Ifoo接口的属性值包含多种类型:string、number和boolean(如果只有单一类型,例如全部都是string或者number等,则不会出现报错),所以typescript把非字面量的属性的值的类型认为是never。同时因为我们的val偷懒用了any,也没有办法很好的匹配上Ifoo定义的属性指的类型。

解决方法

使用typeof

interface Ifoo{
  name: string;
  age: number;
  weight: number;
  isEngineer: boolean;
}

function setProp (foo: Ifoo, key: keyof Ifoo, val: Ifoo[keyof Ifoo]) {
  (<typeof val>foo[key]) = val; // 报错消失
}

显式的告诉typescript我这里的属性值的类型转换为赋赋予的值的类型。

更完美的解决方法

感谢评论区的提醒,上面的解决方案在某些场景下还是会存在问题,例如

var foo: Ifoo = {
name: 'Daisy',
age: 12,
weight: 20,
isEngineer: false
}

setProp(foo, 'name', 15)

针对值为string类型的name字段赋值,本意是希望限制string类型传入,但是由于Ifoo[keyof Ifoo]是把所有属性的类型的联合,相当于只要是符合属性中的任一种类型,则会认为正确。

这明显和我们的本意不一样,所以改为使用泛型的方案:

interface Ifoo{
  name: string;
  age: number;
  weight: number;
  isEngineer: boolean;
}

function setProp<T, K extends keyof T> (foo: T, key: K, val: T[K]) {
    foo[key] = val;
}