打字稿动态 getter/setter
Typescript dynamic getter/setter
我一直在尝试让 Typescript 推断动态创建的 getter 和 setter 的类型。我有一个 class 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
类型
这是正确实施的方法吗?
你必须把class的public接口和实现细节分开考虑。
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
方法应该只是一个实现细节。它们是您的 class 实现您想要的接口的方式——这是为了让 myClass.containers.first
成为类型为 Content
的可读和可写的 属性。 public那个接口的“契约”很简单。
type ContainersMap = { [key: string]: Content }
或使用一组已知的键:
type ContainersMap<K extends string> = { [key in K]: Content } // same as Record<K, Content>
由于没有初始值,您可能希望将属性设为可选。
您需要通用 class 以便 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;
我一直在尝试让 Typescript 推断动态创建的 getter 和 setter 的类型。我有一个 class 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
类型
这是正确实施的方法吗?
你必须把class的public接口和实现细节分开考虑。
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
方法应该只是一个实现细节。它们是您的 class 实现您想要的接口的方式——这是为了让 myClass.containers.first
成为类型为 Content
的可读和可写的 属性。 public那个接口的“契约”很简单。
type ContainersMap = { [key: string]: Content }
或使用一组已知的键:
type ContainersMap<K extends string> = { [key in K]: Content } // same as Record<K, Content>
由于没有初始值,您可能希望将属性设为可选。
您需要通用 class 以便 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;