【问题标题】:Typescript | Typing a dynamic class打字稿 |键入动态类
【发布时间】:2020-05-21 03:54:10
【问题描述】:

我有一个有多个子类的类,它几乎是一个神奇的类,并且做了一些逻辑来帮助我与 json api 集成。

这个例子中的类被称为Resource,这个类的工作就是能够像使用 Laravel 模型一样使用它。所以我扩展了这个类,方法显然可以从那个类中使用,但是我在子类上有了变异器和其他逻辑,举这个例子。

class Resource {
    constructor(attributes: Attributes) {
        const keys: string[] = Object.keys(attributes);

        keys.forEach((attributeKey: string) => {
            this.setAttribute(attributeKey, attributes[attributeKey]);

            Object.defineProperty(this, attributeKey, {
                get: () => this.getAttribute(attributeKey),
                set: (value: any) => this.setAttribute(attributeKey, value);
            });
        });
    }

    ...
}
class User extends Resource {
    setFirstNameAttribute(value: string): void {
        this.attributes.firstName = value.toUpperCase();
    }
}

这会将类的 firstName 属性转换为全大写,这是由 Resource 类的构造函数中定义的 getter 和 setter 完成的。

这一切都很完美,例如......

const user = new User({ firstName: 'John', lastName: 'Doe' });
console.log(user.firstName);

实际上会输出JOHN,因此父类内部的所有逻辑都可以正常工作。

我发现自己遇到的唯一问题是类型,因为属性实际上是动态设置的,我似乎无法找到获取这种类型的方法,因为我希望类上的方法被类型提示所以@ 987654327@ 不会出错,user.firstName 也不会出错,但因为 firstName 不是实际属性,我无法找到提示的方法。

我尝试设置类似class Resource<T> 的内容,但我不确定我是否完全理解这一点。

【问题讨论】:

  • const user:User = new User(....) 怎么样?您的用户类是什么样的?您是否公开了您的成员?
  • @Joel 我不敢相信我从来没有真正想过这个。你是说做类似...class User extends Resource { firstName!: string; lastName!: string }
  • @tallent123 该解决方案也可以正常工作。然后,您可以从类中提取字段以获得正确类型的构造函数..

标签: typescript types


【解决方案1】:

您可以添加一个泛型类型参数来表示属性的类型。天真地,您会认为可以将此泛型类型参数添加到类的扩展或实现子句中(即class ResourceBase<T> extends T),但由于各种原因,这是不可能的。

您可以做的是定义基类,而不能动态访问密钥(即user.firstName 不起作用)。然后,您可以将此基转换为通用构造函数,该构造函数返回基类和类型参数 (new <T>(attr: T) => ResourceBase<T> & T) 的交集。继承此构造函数时,只要提供具体类型(即不是类型参数),一切都应该正常。

注意:还在K extends keyof T 中将getAttributesetAttribute 设为泛型,以完全捕获每个属性的键和键类型。

class ResourceBase<T> {
    constructor(protected attributes: T) {
        const keys = Object.keys(attributes) as Array<keyof T>;

        keys.forEach((attributeKey) => {
            this.setAttribute(attributeKey, attributes[attributeKey]);

            Object.defineProperty(this, attributeKey, {
                get: () => this.getAttribute(attributeKey),
                set: (value: any) => this.setAttribute(attributeKey, value);
            });
        });
    }
    getAttribute<K extends keyof T>(key: K) : T[K] {
        return this.attributes[key];
    }
    setAttribute<K extends keyof T>(key: K, value: T[K]) {
        this.attributes[key] = value;
    }
}

const Resource: new <T>(attr: T) => ResourceBase<T> & T = ResourceBase as any


class User extends Resource<{
    firstName: string,
    lastName: string
}> {
    setFirstNameAttribute(value: string): void {
        this.attributes.firstName = value.toUpperCase();
    }
}


const user = new User({ firstName: 'John', lastName: 'Doe' });
console.log(user.firstName);

Playground Link

【讨论】:

  • 这似乎是我正在寻找的东西,我喜欢它的布局,我只是想更多地理解它。 keys.forEach((attributeKey) =&gt; 行 attributeKey 是什么类型,因为最初它是 string 但现在如果我将其保留为字符串会产生 Typescript 错误,它们都会简单地变成 keyof T
  • @tallent123 是的,现在应该是 string 而不是 keyof T。我通常避免在 TS 中进行大量显式输入。相反,我觉得它没有增加任何价值
  • 这还能让我访问与字符串关联的方法吗?例如value.match,如果我使用keyof T,它仍然可用吗?
  • keyof T 实际上也可以是 numbersymbol
  • 到目前为止一切都很好,但是当我做类似attributeKey. 的事情时,我只有.toString() 方法可用,有没有办法避免这种情况并在这个类中获得实际的类型提示?
猜你喜欢
  • 2020-03-09
  • 1970-01-01
  • 2019-11-28
  • 2019-11-10
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
相关资源
最近更新 更多