属性 字符串 属性 名称的打字稿类型安全更新
Typescript type safe update of property by string property name
我需要通过字符串 属性 名称更新 属性 或 class 的值。我首先通过以下方法确保 属性 名称有效:
export class ClientDTO {
...
static isValidPropertyName(name: string): name is keyof ClientDTO {
return (name as keyof ClientDTO) !== undefined
}
}
然后在另一个 class 我这样做:
foo(key: string, newValue: string) {
if (!ClientDTO.isValidPropertyName(key)) {
return
}
if (newValue !== this.originalClient[key]) {
// @ts-ignore
this.originalClient[key] = newValue
}
}
查找现在运行良好,但要进行更新,我必须将 // @ts-ignore
放在那里,我真的很想弄清楚如何正确地做到这一点,而不必在那里忽略.
我开启了严格检查,所以我得到了错误
TS2322: Type 'any' is not assignable to type 'never'
return (name as keyof ClientDTO) !== undefined
这不会检查 name
是 ClientDTO 的键。它断言它是,然后检查该字符串是否未定义。在 playground.
中尝试
即使这样做有效,它也只会检查该字符串是否是 ClientDTO 的有效密钥,但不会说明它是哪一个。因此,Typescript 会检查您设置的类型是否可以安全地分配给 ClientDTO 的 any 键;由于 ClientDTO 包含 "a mix of types",其中包括 "String, Boolean, date and number",因此要分配的唯一安全值是 never
.
为了安全地分配 newValue: string
,您需要一个函数来确保在运行时您的 key
是 string
类型的 属性,这可能涉及一些重复。
class MyClass {
constructor(
public a: string,
public b: string,
public c: string,
public x: number,
public y: number,
public z: number) { }
}
function isStringProperty(propertyName: string): propertyName is "a" | "b" | "c" {
return ["a", "b", "c"].indexOf(propertyName) >= 0;
}
function isNumberProperty(propertyName: string): propertyName is "x" | "y" | "z" {
return ["x", "y", "z"].indexOf(propertyName) >= 0;
}
function setString(dto: MyClass, key: string, newValue: string) {
if (isStringProperty(key)) {
dto[key] = newValue;
}
}
function setNumber(dto: MyClass, key: string, newValue: number) {
if (isNumberProperty(key)) {
dto[key] = newValue;
}
}
另请参阅:
问题是你的自定义类型保护:
isValidPropertyName(name: string): name is keyof ClientDTO { ... }
正在防范ClientDTO
的任何键,所以当你尝试使用它时:
this.originalClient[key] = newValue // newValue is type string
TypeScript 正在尝试为 this.originalClient[key]
的值推断正确的类型。由于 key
可以是 ClientDTO
的 any 键,您分配给它的值必须可以分配给这些键的所有值类型。由于这些键混合了多种值类型,因此唯一可分配给所有这些键的类型是底部类型 never
;无法分配任何内容的类型,因此会出现错误。
要解决此问题,请注意您给 newValue
输入 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
}
}
我需要通过字符串 属性 名称更新 属性 或 class 的值。我首先通过以下方法确保 属性 名称有效:
export class ClientDTO {
...
static isValidPropertyName(name: string): name is keyof ClientDTO {
return (name as keyof ClientDTO) !== undefined
}
}
然后在另一个 class 我这样做:
foo(key: string, newValue: string) {
if (!ClientDTO.isValidPropertyName(key)) {
return
}
if (newValue !== this.originalClient[key]) {
// @ts-ignore
this.originalClient[key] = newValue
}
}
查找现在运行良好,但要进行更新,我必须将 // @ts-ignore
放在那里,我真的很想弄清楚如何正确地做到这一点,而不必在那里忽略.
我开启了严格检查,所以我得到了错误
TS2322: Type 'any' is not assignable to type 'never'
return (name as keyof ClientDTO) !== undefined
这不会检查 name
是 ClientDTO 的键。它断言它是,然后检查该字符串是否未定义。在 playground.
即使这样做有效,它也只会检查该字符串是否是 ClientDTO 的有效密钥,但不会说明它是哪一个。因此,Typescript 会检查您设置的类型是否可以安全地分配给 ClientDTO 的 any 键;由于 ClientDTO 包含 "a mix of types",其中包括 "String, Boolean, date and number",因此要分配的唯一安全值是 never
.
为了安全地分配 newValue: string
,您需要一个函数来确保在运行时您的 key
是 string
类型的 属性,这可能涉及一些重复。
class MyClass {
constructor(
public a: string,
public b: string,
public c: string,
public x: number,
public y: number,
public z: number) { }
}
function isStringProperty(propertyName: string): propertyName is "a" | "b" | "c" {
return ["a", "b", "c"].indexOf(propertyName) >= 0;
}
function isNumberProperty(propertyName: string): propertyName is "x" | "y" | "z" {
return ["x", "y", "z"].indexOf(propertyName) >= 0;
}
function setString(dto: MyClass, key: string, newValue: string) {
if (isStringProperty(key)) {
dto[key] = newValue;
}
}
function setNumber(dto: MyClass, key: string, newValue: number) {
if (isNumberProperty(key)) {
dto[key] = newValue;
}
}
另请参阅:
问题是你的自定义类型保护:
isValidPropertyName(name: string): name is keyof ClientDTO { ... }
正在防范ClientDTO
的任何键,所以当你尝试使用它时:
this.originalClient[key] = newValue // newValue is type string
TypeScript 正在尝试为 this.originalClient[key]
的值推断正确的类型。由于 key
可以是 ClientDTO
的 any 键,您分配给它的值必须可以分配给这些键的所有值类型。由于这些键混合了多种值类型,因此唯一可分配给所有这些键的类型是底部类型 never
;无法分配任何内容的类型,因此会出现错误。
要解决此问题,请注意您给 newValue
输入 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
}
}