技术解析

如何让 TypeScript 正确推导计算属性名的引用?
0
2021-09-03 08:16:34
idczone

比如有这么一个类:

class Something {
  set (key: string, val: any) {
    this['set$' + key](val)
    // 上面这句会有类型错误提示
  }

  set$name (name: string) {...}

  set$age (age: number) {...}
}

在启用 noImplicitAny 时 TS 会报错:'Something' has no index signature.(在 this[...] 部分)。然而并不知道如何标注类型。

key 可能的取值有一个明确的列表,其类型在这里也可以写成 key: 'name' | 'age'。所以虽然这种计算属性名是运行时逻辑,但理论上是可以在编译时分析的(字面量和固定值的计算)。于是问题就是 TS 现在是否支持这种分析?

PS: 刚注意到 V2EX 还没有 TS 节点。


不可能的,你把 set$name 方法前的 set$去掉还有一点可能

不可能

目前还不支持这种类型推断,可能是因为太复杂了
但是现在是可以标注类型的: https://www.typescriptlang.org/play/index.html#src=class%20Something%20%7B%0A%20%20set(this%3A%20%7Bset%24name%3A%20any%2C%20set%24age%3A%20any%7D%2C%20key%3A%20'name'%20%7C%20'age'%2C%20val%3A%20any)%20%7B%0A%20%20%20%20this%5B'set%24'%20%2B%20key%5D(val)%0A%20%20%20%20%2F%2F%20%E4%B8%8A%E9%9D%A2%E8%BF%99%E5%8F%A5%E4%BC%9A%E6%9C%89%E7%B1%BB%E5%9E%8B%E9%94%99%E8%AF%AF%E6%8F%90%E7%A4%BA%0A%20%20%7D%0A%0A%20%20set%24name%20(name%3A%20string)%20%7B%7D%0A%0A%20%20set%24age%20(age%3A%20number)%20%7B%7D%0A%7D

Options 里开启 noImplicitAny 后,错误提示出现。

```typescript
class Foo {
[P: string]: any
set(name: string, value: any) {
this['key' + name](value)
}
foo() {
console.log('foo')
}
}
const a = new Foo()
a.foo()
// error
// a.foo(1)
```
[Playground]( https://www.typescriptlang.org/play/#src=class%20Foo%20%7B%0D%0A%20%20%5BP%3A%20string%5D%3A%20any%0D%0A%20%20set(name%3A%20string%2C%20value%3A%20any)%20%7B%0D%0A%20%20%20%20this%5B'key'%20%2B%20name%5D(value)%0D%0A%20%20%7D%0D%0A%0D%0A%20%20foo()%20%7B%0D%0A%20%20%20%20console.log('foo')%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aconst%20a%20%3D%20new%20Foo()%0D%0Aa.foo()%0D%0A%2F%2F%20error%0D%0A%2F%2F%20a.foo(1))

这是不是和 一个效果……
再尝试搜索了一下,放弃从 TS 寻找原生解决方案了。个人觉得这也算 JS 编码的常见套路,希望 TS 早日能支持这个特性吧。

这个虽然是 js 编码的常见套路, 但是不能称得上一个好套路, 所以不支持也无可厚非.
如果改成这样那就可以支持
```
type Properties = {
name:string,
age:number
}
class Foo {
set(key:k, value: Properties[k]){
this.properties[key] = value
}
properties:Properties
}
const f = new Foo()
f.set("name",233) //报错
```

我是想实现 3 点效果:单一 API ( set 方法)、set 过程有副作用(不是赋值这么简单)、派生类可以覆盖其中某个 set 方法。暂时没想到 TS 里有什么好的套路。

我是想用 TS 实现这段代码的效果:
https://jsfiddle.net/SilentDepth/3ydpra7q/

这是标准的写法呀, 类型的 key 可以是不定的, 类也是一样, 比如
type Map = { [P: string]: T }
和 完全不是一个东西, 代码里面理论上一定不要用 这个东西

我明白,我是说,这个 index signature 只是告诉编译器兼容未知的引用,并没有帮助类型推断,从结果上和 ts-ignore 没啥区别。

这个性质是完全不一样的, ts-ignore 是跳过 ts 的静态类型检测, 意味着无论是对的还是错的都会被跳过, 这样带来的效果就是写这样的 ts 还不如直接写 js, 而上面说的形式是你明确知道这个类型的数据结构的.
其实还有一种方法, 就是直接用 any 跳过:
set(key, value) {
(this as any)['key' + key](value)
}

那还不如用 JS 写

这意思是,这样的需求不适合用 TS 实现吗?(需求要点见 )

现在 ts 有个尴尬的问题,这样是可以的
https://www.typescriptlang.org/play/index.html/>然而这样就不行
https://www.typescriptlang.org/play/index.htmlts 3.1 是不好实现的

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服