如何解决打字稿动态getter/setter
我一直在试图让 Typescript 推断动态创建的 getter 和 setter 的类型。我有一个带有 MyClass
地图的 containers
类:
type Container = {
get: () => Content
set: (value: Content) => void
}
type ContainersMap = { [key: string]: Container }
class MyClass {
containers: ContainersMap
constructor(names: string[]) {
this.containers = {} as ContainersMap
names.forEach(name => {
Object.defineProperty(this.containers,name,{
get() { return read(name) },set(value) { write(name,value) }
})
})
Object.freeze(this.containers)
}
}
接下来在我的代码中我想这样使用它:
let someContent: Content = {/* some content */}
let myClass = new MyClass(['first','second'])
// Getting type error in line below because TS thinks I try assign Content to Container
myClass.containers['first'] = someContent
我发现一个可能的解决方案是定义 type ContainersMap = { [key: string]: Content }
,但我不喜欢这个解决方案,因为在我看来它没有反映实际的 ContainersMap
类型
这是一种正确实施的方法吗?
解决方法
您必须将类的公共接口与实现细节分开考虑。
type Container = {
get: () => Content
set: (value: Content) => void
}
该类型定义了一个具有 get
和 set
属性的对象。这意味着您可以调用 myClass.containers.first.set(someContent)
。但是您不能执行 myClass.containers.first = someContent
,因为 myClass.containers.first
应该属于 Container
类型,而不是 Content
类型。
get
和 set
方法应该只是一个实现细节。它们是您的类实现您想要的接口的方式——这使 myClass.containers.first
成为 Content
类型的可读和可写属性。该接口的公共“契约”非常简单。
type ContainersMap = { [key: string]: Content }
或使用一组已知的键:
type ContainersMap<K extends string> = { [key in K]: Content } // same as Record<K,Content>
由于没有初始值,您可能希望将属性设为可选。
您将需要一个通用类,以便 ContainersMap
了解您的特定键。我不确定您的代码中的 read
和 write
方法是什么,所以我只保留它们。这基本上就是你想要的:
class MyClass<K extends string> {
containers: ContainersMap<K>
constructor(names: K[]) {
this.containers = {} as ContainersMap<K>
names.forEach(name => {
Object.defineProperty(this.containers,name,{
get() { return read(name) },set(value) { write(name,value) }
})
})
Object.freeze(this.containers)
}
}
您根本不再需要 Container
类型。
这允许您获取和设置已知属性:
let myClass = new MyClass(['first','second'])
myClass.containers.first = someContent;
console.log(myClass.containers.first);
但您无法访问其他属性:
// Error: Property 'third' does not exist on type 'ContainersMap<"first" | "second">'
myClass.containers.third = someContent;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。