Ember 3.15组件用变量属性名调用set/get时TypeScript报错咨询
这事儿其实是静态类型检查和动态语言运行时的典型冲突,我来给你掰扯清楚:
核心原因
运行时正常的原因
JavaScript本身是动态类型语言,Ember的set和get方法在运行时完全不关心属性名是不是提前定义好的——只要你传了字符串,它就会在对象上设置/读取对应的属性,所以代码跑起来完全没问题。
TypeScript报错的原因
TypeScript是静态类型检查工具,它的核心任务就是在代码运行前帮你规避类型错误。当你用一个动态的propertyName变量去调用set(this, propertyName, value)时,TS没办法确定这个变量对应的字符串是不是你的组件类上已经声明过的合法属性名。
举个例子,假设你的组件类只声明了name和age两个属性,但propertyName有可能被赋值成"invalidProp",TS就会担心你在设置一个不存在的属性,所以会抛出类型错误,哪怕你实际运行时不会出现这种情况。
解决办法
针对这个问题,有几个常用的修复方式,你可以根据自己的场景选:
1. 给propertyName指定明确的类型
如果你能确定propertyName只会是组件类上已有的属性名,那可以用keyof操作符来限定它的类型:
// 先假设你的组件类叫MyComponent class MyComponent extends Component { // 声明你的组件属性 name = ''; age = 0; @action someMethod() { const value = 'test'; // 限定propertyName只能是MyComponent的属性键 let propertyName: keyof MyComponent = 'name'; // 或者其他合法属性名 set(this, propertyName, value); } }
这样TS就能确认propertyName是合法的,不会再报错。
2. 添加索引签名(适用于动态属性)
如果你的组件需要支持动态添加的属性(也就是提前没声明的属性),可以在组件类里添加索引签名,告诉TS这个类允许任意字符串作为属性名:
class MyComponent extends Component { // 声明索引签名,这里的any可以换成你实际需要的类型 [key: string]: any; @action someMethod() { const value = 'test'; let propertyName = 'dynamicProp'; set(this, propertyName, value); } }
不过这种方式会稍微弱化类型检查,所以如果能明确属性范围的话,优先用第一种方法。
3. 使用类型断言(快速修复)
如果你只是想快速绕过TS的检查,确认自己的代码没问题,可以用类型断言把propertyName转换成keyof this:
@action someMethod() { const value = 'test'; let propertyName = 'someProp'; set(this, propertyName as keyof this, value); }
这种方式比较灵活,但要注意——你得自己保证propertyName确实是合法的属性名,不然TS就帮不了你排查错误了。
同样的逻辑也适用于get(this, propertyName)的情况,解决办法完全一致。
内容的提问来源于stack exchange,提问作者Ian Kirkpatrick

