简体   繁体   English

TypeScript - 键入一个返回绑定到类的静态方法的函数

[英]TypeScript - typing a function that returns static methods bound to a class

I'm trying to type the following code using TypeScript:我正在尝试使用 TypeScript 键入以下代码:

const createClass = ({ constructor, staticMethods }) => {
  // constructor can be undefined, in which case we use an empty func
  const ReturnClass = constructor || function () {};

  Object.keys(staticMethods).forEach(methodName => {
    ReturnClass[methodName] = staticMethods[methodName].bind(ReturnClass);
  });

  return ReturnClass;
}

You would use createClass like so:您可以像这样使用createClass

const MyClass = createClass({
  constructor: function () {
    console.log("hello");
  },
  staticMethods: {
    test() {
      return new this(); // should return a new instance of MyClass
    }
  }
});

MyClass.test(); // should log to console and return new instance of MyClass

I'm struggling to get the types working.我正在努力让这些类型工作。 This is what I've currently got:这是我目前所拥有的:

const createEntity = <T, U>({
  constructor,
  staticMethods
}: {
  constructor: T => void; // not sure what to do here
  staticMethods: U;       // how can I make U refer to an object of methods?
}): {
  new(...T): any,         // I saw that this is how you do a constructor, but I'm unsure
  ...U                    // not sure how to spread U
} => {

Any advice would be greatly appreciated.任何建议将不胜感激。

For the signature of createClass() I think I would use overloads , both to handle the case with a constructor property and the case without (for some reason just making it optional doesn't work well), and to loosen the types so that the implementation can be written without much difficulty.对于createClass()的签名,我想我会使用重载,既可以处理带有constructor属性的情况,也可以处理没有constructor属性的情况(出于某种原因,只是使其可选不能很好地工作),并放宽类型,以便可以毫不费力地编写实现。 Here it is:这里是:

function createClass<
    A extends any[],
    T extends object,
    M extends Record<keyof M, Function>>(arg:
        {
            constructor: ((this: T, ...args: A) => void),
            staticMethods: M & ThisType<M & (new (...args: A) => T)>
        }):
    (((new (...args: A) => T) & M));

function createClass<
    M extends Record<keyof M, Function>>(arg:
        {
            staticMethods: M & ThisType<M & (new () => object)>
        }):
    (((new () => object) & M));

function createClass({ constructor, staticMethods }:
    { constructor?: Function, staticMethods: Record<string, Function> }
) {
    const ReturnClass = (constructor || function () { }) as
        (Function & Record<string, Function>)
    Object.keys(staticMethods).forEach(methodName => {
        ReturnClass[methodName] = staticMethods[methodName].bind(ReturnClass);
    });

    return ReturnClass;
}

I'm using a lot of generics... generally, A is the list of arguments to the constructor, T is the object type the constructor is dealing with, and M is the static methods object.我使用了很多泛型......通常, A是构造函数的参数列表, T是构造函数正在处理的对象类型, M是静态方法对象。 I use the ThisType<T> utility type to help the compiler understand what you mean by this inside the staticMethods method implementations you pass to createClass() .我用的是ThisType<T>实用型,以帮助编译器明白你的意思了this里面staticMethods传递给方法实现createClass() Instead of trying to explain each line, I'll leave it at that.我不会试图解释每一行,而是将它留在那里。

Let's see if it works.让我们看看它是否有效。 Here's a more general MyClass :这是一个更通用的MyClass

interface MyClass {
    x: string;
}
const MyClass = createClass({
    constructor: function (this: MyClass) {
        console.log("hello");
        this.x = "hey";
    },
    staticMethods: {
        test() {
            return new this();
        },
        anotherTest() {
            this.test();
        }
    }
});

const m = MyClass.test(); // hello
console.log(m.x); // hey
MyClass.anotherTest(); // hello

Here I've specified that the constructor function property will actually be constructing an object of type MyClass , an interface I defined as having an x property of type string .在这里,我已经指定constructor函数属性实际上将构造一个MyClass类型的对象,一个我定义为具有string类型的x属性的接口。 You can see that this works;你可以看到这是有效的; m is known to be of type MyClass , and the MyClass constructor is known to have static methods test() and anotherTest() .已知m属于MyClass类型,并且已知MyClass构造函数具有静态方法test()anotherTest() And inside the implementation of these methods we can use this both as the constructor ( new this ) and the thing-with-static-methods ( this.test() ).在这些方法的实现中,我们可以将this用作构造函数( new this )和带有静态方法的事物( this.test() )。

And let's just test the constructor -is- undefined scenario:让我们只测试constructor -is- undefined场景:

const EmptyCtor = createClass({
    staticMethods: {
        test() {
            console.log("goodbye");
            return new this();
        }
    }
});
const e = EmptyCtor.test(); // goodbye
console.log(typeof e); // object

Also looks good.看起来也不错。 Okay, I hope this helps give you direction;好的,我希望这有助于给你指明方向; good luck!祝你好运!

Playground link 游乐场链接

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

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