简体   繁体   English

Typescript 动态getter/setter

[英]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 属性getset 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. getset方法应该只是一个实现细节。 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;

Typescript Playground Link Typescript 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM