首页 > 解决方案 > 打字稿魔法属性

问题描述

我开始使用 Typescript 并且有点卡住了。是否可以允许 TypeScript 类使用在运行时添加的属性?我正在使用这样的东西:

interface IObject {
   [key: string]: any;
}

class A {
    private extends: IObject = {};


    constructor() {
        return new Proxy(this, {
            get: (target: any, name: string) => (name in this.extends) ? this.extends[name] : target[name],
        })
    }

    public extend(key: any, argument: object): A {
       this.extends = Object.assign(this.extends, {
           [key]: argument
       });

       return this;
   }
}

所以这个类就像我的应用程序中的可扩展上下文。我可以在运行时在此处添加任何属性,aInstance.extend('property', {a: 1})并像这样使用它们aInstance.property。在纯 JS 中,它将{a: 1}按预期返回,但 TypeScript 在执行此操作时会引发错误(“A”类型上不存在 Property 'property'。)。那么有什么办法可以避免这种情况吗?我知道我可以使用// @ts-ignore,但我不想使用它,因为维护代码会更加困难。

将不胜感激在这里的任何建议。谢谢 :)

标签: typescript

解决方案


我认为不可能完全按照您的意愿去做并保持类型安全。一种选择是执行以下操作:

class A {
    private extends: Record<string, any> = {};

    constructor() {
        return new Proxy(this, {
            get: (target: any, name: string) => (name in this.extends) ? this.extends[name] : target[name],
        })
    }

    public extend<T extends object>(obj: T): A & T {
       this.extends = Object.assign(this.extends, obj);

       // necessary evil, but safe because we know we are returning A & T due to the proxy
       return this as any;
   }
}

let aInstance = new A()
let extended = aInstance.extend({"test": "foo"})

console.log(extended.test)

基本上,extend 方法现在返回的类型是 A 和 T 的并集。缺点是只有 extend 函数的返回值将被键入为具有额外属性。

我会考虑为什么首先需要在运行时扩展类型?

如果您提前知道属性名称,那么只需将它们添加到类中。否则,听起来地图可能更适合这项工作。


推荐阅读