首页 > 解决方案 > 通过字符串属性名称对属性进行打字稿类型安全更新

问题描述

我需要通过字符串属性名称更新类的属性值。我首先通过此方法确保属性名称有效:

export class ClientDTO {
    ...

    static isValidPropertyName(name: string): name is keyof ClientDTO {
        return (name as keyof ClientDTO) !== undefined
    }
}

然后在另一堂课上,我正在这样做:

foo(key: string, newValue: string) {
  if (!ClientDTO.isValidPropertyName(key)) {
    return
  }

  if (newValue !== this.originalClient[key]) {
    // @ts-ignore
    this.originalClient[key] = newValue
  }
}

查找现在运行良好,但要进行更新,我必须把它放在// @ts-ignore那里,我真的很想弄清楚如何正确地做到这一点,而不必在那里忽略。

我打开了严格的检查,所以我得到了错误

TS2322:类型“任何”不可分配给类型“从不”

标签: typescriptkeyoftypescript3.8

解决方案


问题是您的自定义类型保护:

isValidPropertyName(name: string): name is keyof ClientDTO { ... }

正在防范 的任何键ClientDTO,因此当您尝试使用它时:

this.originalClient[key] = newValue // newValue is type string

TypeScript 试图推断this.originalClient[key]. 由于key可以是的任何ClientDTO,因此您分配给它的值必须可分配给这些键的所有值类型。由于这些键的值类型混合在一起,因此唯一可以分配给它们的类型是底部类型never;无法为其分配任何内容的类型,因此会出现错误。

要解决此问题,请注意您提供newValuetype string。因此,将您的类型保护限制为只有那些ClientDTO谁的值是字符串的键:

type KeysWithStringValues<T extends {}> = {
    [K in keyof T]: T[K] extends string ? K : never;
}[keyof T];

class ClientDTO {
    /* ... */
    static isValidPropertyName(name: string): name is KeysWithStringValues<ClientDTO> {
        // Make sure to replace this with code that ACTUALLY enforces
        // the above constraint.
        return name !== undefined
    }
}

游乐场链接。


推荐阅读