[英]Typescript dynamic getter/setter
I'm stuck trying to make Typescript infer types of dynamically created getter and setter.我一直在尝试让 Typescript 推断动态创建的 getter 和 setter 的类型。 I have a class
MyClass
with containers
map:我有一个带有
containers
map 的 class MyClass
:
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)
}
}
And next in my code I want to use it like this:接下来在我的代码中我想这样使用它:
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
A possible solution I found is to define type ContainersMap = { [key: string]: Content }
but I dislike this solution because in my opinion it doesn't reflect the actual ContainersMap
type我找到的一个可能的解决方案是定义
type ContainersMap = { [key: string]: Content }
但我不喜欢这个解决方案,因为在我看来它没有反映实际的ContainersMap
类型
Is it a way to implement this properly?这是正确实施的方法吗?
You have to think about the public interface of the class separately from the implementation details.您必须将 class 的公共接口与实现细节分开考虑。
type Container = {
get: () => Content
set: (value: Content) => void
}
This type defines an object with properties get
and set
.这种类型定义了一个 object 属性
get
和set
。 It means that you can call myClass.containers.first.set(someContent)
.这意味着您可以调用
myClass.containers.first.set(someContent)
。 But you cannot do myClass.containers.first = someContent
because myClass.containers.first
is supposed to be of type Container
, not of type Content
.但是你不能做
myClass.containers.first = someContent
因为myClass.containers.first
应该是Container
类型,而不是Content
类型。
The get
and set
methods should be just an implementation detail. get
和set
方法应该只是一个实现细节。 They are the way that your class implements the interface that you want -- which is for myClass.containers.first
to be a readable and writable property of type Content
.它们是您的 class 实现您想要的接口的方式——这是为了让
myClass.containers.first
成为Content
类型的可读和可写属性。 The public "contract" of that interface is very simple.该接口的公共“合同”非常简单。
type ContainersMap = { [key: string]: Content }
or with a known set of keys:或者使用一组已知的键:
type ContainersMap<K extends string> = { [key in K]: Content } // same as Record<K, Content>
Since there is no initial value, you might want to make the properties optional.由于没有初始值,您可能希望将属性设为可选。
You will need a generic class in order for the ContainersMap
to know about your specific keys.您将需要一个通用的 class 以便
ContainersMap
了解您的特定密钥。 I'm not sure what the read
and write
methods are in your code so I'm just leaving them be.我不确定您的代码中的
read
方法是什么,所以我只是让它们保持write
。 This is basically what you want:这基本上就是你想要的:
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)
}
}
You do not need the type Container
at all anymore.您根本不再需要
Container
类型。
This allows you to get and set known properties:这允许您获取和设置已知属性:
let myClass = new MyClass(['first', 'second'])
myClass.containers.first = someContent;
console.log(myClass.containers.first);
But you cannot access other properties:但是您不能访问其他属性:
// Error: Property 'third' does not exist on type 'ContainersMap<"first" | "second">'
myClass.containers.third = someContent;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.