typescript - 定义从继承类动态生成的属性

标签 typescript typescript-definitions .d.ts

我正在尝试为我的项目生成 d.ts 声明文件。

有两个类确实计算繁重的逻辑,其余的类继承自它们。子类的属性不是在对象本身上定义的,而是在类内名为 defaultsgetter 上定义的,并在运行时定义。

基础模型

abstract class BaseModel {}
abstract class Model extends BaseModel {}

继承模型

class Example extends Model {
    get defaults() {
       return {
            someProp: 1,
            anotherProp: 2
       }
    }
}

我的代码运行良好,但动态添加的属性没有自动完成。我尝试将以下内容添加到 d.ts 文件以使其了解动态属性,但它似乎不起作用。

index.d.ts

class Model<T> extends BaseModel {
    [P in keyof T]: T[P]
}

type ExampleType = {
    someProp : number
    anotherProp : number
}

class Example extends Model<ExampleType> {}

如何向继承的类添加属性定义,而无需手动定义它们?

最佳答案

您不能直接执行此操作,如果您使用额外的函数创建类,则可以执行此操作,该函数会改变创建类以添加属性:

type ReplaceInstanceType<T extends new (...args: any[])=> any, TNewInstance> = 
    T extends new (...args: infer U)=> any ? 
        new (...args: U) => TNewInstance : 
        never;

function createModel<T extends new (...args: any[])=> any>(modelClass: T) : 
    ReplaceInstanceType<T, InstanceType<T> & InstanceType<T>['defaults']> {
    return modelClass as any;
}

注意这使用了 3.0 功能 Tuples in rest parameters and spread expressions 对于 ReplaceInstanceType 的 2.9、2.8 版本,请参阅 this answer

我们可以通过以下两种方式之一使用它:

直接使用createModel的输出:

const Example = createModel(class extends Model {
    constructor (data: string) {
        super();
        // this.anotherProp // Not ok we can't access teh extra fields inside the class
    }
    get defaults() {
        return {
            someProp: 1,
            anotherProp: 2
        }
    }
});
new Example(``).anotherProp // we can create the class and access the extra fields
new Example("").someProp

这样做的缺点是额外的字段不能从类本身内部使用,这在某些情况下可能是一个问题。

第二种用法是在新类的extends子句中使用createModel,并且只在我们传递给extends的类中定义默认值,在外部类中添加额外的方法:

class Example extends createModel(class extends Model {
    get defaults() {
        return {
            someProp: 1,
            anotherProp: 2
        }
    }
}) {
    constructor(data: string) {
        super();
        this.anotherProp // new properties are accesible
    }
};
new Example(``).anotherProp // we can create the class and access the extra fields
new Example("").someProp

此方法的缺点是实际上创建了 2 个类,我们实际使用的外部类和我们用来添加默认属性的内部类。

编辑

由于 typescript 实际上在强类型和验证键名称方面做得非常好,您也可以考虑使用 get/set 方法来代替,您可以获得良好的代码完成和类型验证,但它有点多详细:

abstract class BaseModel { 
    abstract get defaults();
    get<K extends keyof this['defaults']>(name: keyof this['defaults']): this['defaults'][K]{
        return this[name] as any;
    }
    set<K extends keyof this['defaults']>(name: keyof this['defaults'], value:  this['defaults'][K]) : void{
        this[name] =  value;
    }
}

class Example extends BaseModel {
    get defaults() {
        return {
            someProp: 1,
            anotherProp: 2
        }
    }

};
new Example().get('anotherProp')
new Example().get("someProp") 
new Example().set("someProp", 1) // 
new Example().set("someProp", "1") // error wrong type
new Example().get("someProp2")  // error

关于typescript - 定义从继承类动态生成的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51514486/

相关文章:

javascript - .d.ts 表示具有返回自身的方法的模块

javascript - 使用 fp-ts 删除 Either 数组中的重复项

reactjs - 带有默认主题的 Material-UI React TypeScript 组件样式错误

javascript - 如何在 TypeScript 中访问 jQuery .each 的 "this"?

javascript - 为普通函数添加 d.ts,它返回一个包装函数的函数

javascript - 在 JavaScript 类型文件中声明接口(interface)

typescript - 对于 Typescript 中的返回类型,Object.seal 和 .freeze 是否会提供不同的结果?

javascript - 类的 typescript 定义作为对象属性

typescript - 如何导入顶级元素是非导出命名空间的 Typescript 类型定义文件?

javascript - 如何将带有子项的 react-bootstrap 组件导入 kotlin-js react app