首页 > 解决方案 > Typescript Class inference with statics

问题描述

import { FilterQuery } from "mongodb";

abstract class Base<T extends Base<any>> {
    public static async findOne<T extends Base<any>>(
        this: new (...a: any[]) => T,
        query: FilterQuery<T>
    ): Promise<T> {
        console.log(this.getSomeString()); // currently: "getSomeString" dosnt exists on type "new (...a: any[]) => T"
        // already tried "typeof T" - because "typeof" is not allowed on generics
        // already tried https://stackoverflow.com/a/52358194/8944059 's static solution
        // already tried "T" - which makes "this: any"
        // and i tried various other things, which resulted in "await SomeExtendingClass" cannot be assigned to "this"

        // Edit for @jcalz
        console.log(this.someProtectedMethod()); // error "someProtectedMethod" does not exists on type "this"

        return undefined;
    }

    public static getSomeString(): string {
        this.someProtectedMethod(); // this would work
        return "Hello";
    }

    // Edit for @jcalz
    protected static someProtectedMethod() {
        return "hello2";
    }
}

class SomeExtendingClass extends Base<SomeExtendingClass> {
    public someValue: string;
}

(async () => {
    await SomeExtendingClass.findOne({ someValue: "hi" });
})();

this目标是在能够调用它的同时拥有所有静态变量,同时new this(...args)让(父类)的所有键都可以插入query并且不必删除abstract

已经尝试过:
- https://stackoverflow.com/a/58037767/8944059
- https://stackoverflow.com/a/52358194/8944059的静态解决方案

PS:我不明白打字稿类型的大部分魔力,这就是我问的原因......

PPS:这是我需要的项目

标签: typescriptinheritancetype-inference

解决方案


您的示例代码根本没有说明为什么您需要任何this参数。例如,以下代码甚至可以使用protected静态方法。默认类型this只是typeof Base

import { FilterQuery } from "mongodb";
abstract class Base<T extends Base<any>> {
    public static async findOne<T extends Base<any>>(
        query: FilterQuery<T>
    ): Promise<T> {
        console.log(this.getSomeString()); 
        console.log(this.someProtectedMethod()); 
        return undefined!; // <-- not a Promise
    }

    public static getSomeString(): string {
        return "Hello";
    }
    protected static someProtectedMethod() {
        return "hello2";
    }
}
class SomeExtendingClass extends Base<SomeExtendingClass> {
    public someValue: string = "123"; // needs initialization
}
(async () => {
    await SomeExtendingClass.findOne({ someValue: "hi" });
})();

如果以上内容不足以满足您的用例,请编辑您的示例代码以显示一些中断的地方。大概有一些原因你需要首先添加一个this参数;什么原因?

无论如何,希望有所帮助;祝你好运!

链接到代码


编辑:如果您需要this在内部使用零参数进行构造findOne(),并且不允许findOne()在无法以这种方式构造的类上调用,您可以这样做:

abstract class Base<T extends Base<any>> {
    public static async findOne<T extends Base<any>>(
        this: typeof Base & (new () => any),
        query: FilterQuery<T>
    ): Promise<T> {
        console.log(this.getSomeString());
        console.log(this.someProtectedMethod());
        new this();
        return null!;
    }
    public static getSomeString(): string {
        return "Hello";
    }
    protected static someProtectedMethod() {
        return "hello2";
    }
}

Base.findOne(null!); // error, abstract

class SomeExtendingClass extends Base<SomeExtendingClass> {
    public someValue: string = "123"; // needs initialization
}
SomeExtendingClass.findOne(null!); // okay

class InvalidExtendingClass extends Base<InvalidExtendingClass> {
    constructor(x: string) {
        super();
    }
}
InvalidExtendingClass.findOne(null!); // error, requires an argument

链接到代码

如果您有其他要求(例如允许子类使用不同的构造函数参数),请告诉我。再次祝你好运!


推荐阅读