Typescript按字符串属性名称安全更新属性
我需要用字符串属性名更新类属性的值。我首先通过以下方法确保属性名称有效:Typescript按字符串属性名称安全更新属性,typescript,keyof,typescript3.8,Typescript,Keyof,Typescript3.8,我需要用字符串属性名更新类属性的值。我首先通过以下方法确保属性名称有效: 将类客户端导出到{ ... 静态isValidPropertyName(名称:string):名称是ClientDTO的键{ return(名称为keyof ClientDTO)!==未定义 } } 然后在另一节课上我做了这个: foo(key:string,newValue:string){ 如果(!ClientDTO.isValidPropertyName(键)){ 返回 } if(newValue!==此.orig
将类客户端导出到{
...
静态isValidPropertyName(名称:string):名称是ClientDTO的键{
return(名称为keyof ClientDTO)!==未定义
}
}
然后在另一节课上我做了这个:
foo(key:string,newValue:string){
如果(!ClientDTO.isValidPropertyName(键)){
返回
}
if(newValue!==此.originalClient[键]){
//@ts忽略
this.originalClient[key]=newValue
}
}
查找现在运行得很好,但要进行更新,我必须将/@ts ignore
放在那里,我真的很想知道如何正确地执行此操作,而不必将ignore放在那里
我打开了严格的检查,所以我得到了错误
TS2322:类型“any”不可分配给类型“never”
return(名称为keyof ClientDTO)!=未定义
这不会检查name
是否是ClientDTO的键。它断言它是,然后检查字符串是否未定义。在厨房里试试看
即使这样做有效,它也只会检查字符串是否是ClientDTO的有效键,而不会说明它是哪个键。因此,Typescript检查您正在设置的类型是否可以安全地分配给ClientDTO的任何键;由于ClientDTO包含“多种类型的混合”,包括“字符串、布尔值、日期和数字”,因此唯一可分配的安全值是从不
为了安全地分配newValue:string
,您需要一个函数来确保在运行时键
用于字符串
类型的属性,这可能涉及一些重复
class-MyClass{
建造师(
公共a:字符串,
公共b:字符串,
公共c:字符串,
公共x:号码,
公众y:号码,,
公共z:number){}
}
函数isStringProperty(propertyName:string):propertyName是“a”|“b”|“c”{
返回[“a”、“b”、“c”]。indexOf(propertyName)>=0;
}
函数isNumberProperty(propertyName:string):propertyName是“x”|“y”|“z”{
返回[“x”、“y”、“z”]。indexOf(propertyName)>=0;
}
函数setString(dto:MyClass,key:string,newValue:string){
if(isStringProperty(键)){
dto[键]=新值;
}
}
函数setNumber(dto:MyClass,key:string,newValue:number){
如果(isNumberProperty(键)){
dto[键]=新值;
}
}
另请参见:return(名称为keyof ClientDTO)!=未定义
这不会检查name
是否是ClientDTO的键。它断言它是,然后检查字符串是否未定义。在厨房里试试看
即使这样做有效,它也只会检查字符串是否是ClientDTO的有效键,而不会说明它是哪个键。因此,Typescript检查您正在设置的类型是否可以安全地分配给ClientDTO的任何键;由于ClientDTO包含“多种类型的混合”,包括“字符串、布尔值、日期和数字”,因此唯一可分配的安全值是从不
为了安全地分配newValue:string
,您需要一个函数来确保在运行时键
用于字符串
类型的属性,这可能涉及一些重复
class-MyClass{
建造师(
公共a:字符串,
公共b:字符串,
公共c:字符串,
公共x:号码,
公众y:号码,,
公共z:number){}
}
函数isStringProperty(propertyName:string):propertyName是“a”|“b”|“c”{
返回[“a”、“b”、“c”]。indexOf(propertyName)>=0;
}
函数isNumberProperty(propertyName:string):propertyName是“x”|“y”|“z”{
返回[“x”、“y”、“z”]。indexOf(propertyName)>=0;
}
函数setString(dto:MyClass,key:string,newValue:string){
if(isStringProperty(键)){
dto[键]=新值;
}
}
函数setNumber(dto:MyClass,key:string,newValue:number){
如果(isNumberProperty(键)){
dto[键]=新值;
}
}
另请参见:问题在于您的自定义类型保护:
isValidPropertyName(name: string): name is keyof ClientDTO { ... }
防止ClientDTO的任何键进入
,因此当您尝试使用它时:
this.originalClient[key] = newValue // newValue is type string
TypeScript尝试为this.originalClient[key]
的值推断正确的类型。由于key
可以是ClientDTO
的任何键,因此您分配给它的值必须可分配给这些键的所有值类型。由于这些键的值类型多种多样,因此唯一可分配给所有键的类型是底部类型never
;无法为其分配任何内容的类型,因此会出现错误
要解决此问题,请注意您提供了newValue
类型string
。因此,将您的type guard仅限于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
}
}
键入带有字符串值的键={
[K in keyof T]:T[K]扩展字符串?K:从不;
}[keyof T];
类ClientDTO{
/* ... */
静态isValidPropertyName(名称:string):名称为KeysWithStringValue{
//确保用实际执行的代码替换此代码
//上述限制。
返回名称!==未定义
}
}
问题在于您的自定义类型保护:
isValidPropertyName(name: string): name is keyof ClientDTO { ... }
防止ClientDTO的任何键进入
,因此当您尝试使用它时:
this.originalClient[key] = newValue // newValue is type string
TypeScript尝试为this.originalClient[key]
的值推断正确的类型。由于key
可以是ClientDTO
的任何键,因此您分配给它的值必须可分配给这些键的所有值类型。由于这些键的值类型多种多样,因此唯一可分配给所有键的类型是底部类型never
;无法为其分配任何内容的类型,因此会出现错误
要解决此问题,请注意