如何防止使用错误的参数类型调用重载函数

时间:2019-07-11 16:21:35

标签: typescript overloading

我想为某些JS代码生成打字稿定义,这有点尴尬,因为某些类的静态方法与父级的静态方法同名,但类型签名不同。 herehere的更多详细信息。

我尝试的解决方案是使用这样的通用重载:

export class Parent {
    ...
    static new(arg1: string): Parent
}

export class Child extends Parent {
    ...
    static new(arg1: number, arg2: number): Child
    static new<T, V>(arg1: T): V
}

通过为T调用任意类型的泛型形式可能会滥用此行为,从而导致未定义的行为。有办法防止这种情况吗? Typescript坚持所有具有相同名称且具有相同可见性的重载,因此我不能将泛型重载设置为私有。但是,如果无法将调用限制为使用固定类型的签名之一,则似乎我们错过了重载的最大好处之一。

1 个答案:

答案 0 :(得分:1)

无论您使用重载签名做什么,TypeScript都决定静态方法将从父类(as possibly implied by ES2015)继承,因此您不能仅扩展类并解决此问题。至少直到并且除非TS曾经改变。例如:

namespace Original {
  declare namespace Classes {
    class Parent {
      static method(arg1: string): Parent;
      parentProp: string;
    }

    class Child extends Parent {
      static method(arg1: number, arg2: number): Child;
      static method<T, V>(arg1: T): V;
      childProp: string;
    }
  }

  const child = new Classes.Child();
  const parent = new Classes.Parent();
  const alsoParent: typeof parent = new Classes.Child();

  const childConstructorMethod = Classes.Child.method;
  const parentConstructorMethod = Classes.Parent.method;

  // the following inheritance should hold
  const alsoParentConstructorMethod: typeof parentConstructorMethod = childConstructorMethod;
  // which leads to this even without generics
  alsoParentConstructorMethod("oopsie-daisy");
}

这类似于您的类,并且您可以看到以上内容最终使人们无论您做什么都可以在子构造函数上调用parent-signature方法...因为可以将子静态方法分配给父静态方法类型的变量。那是继承,它限制了您无法使用签名解决的问题。


那你该怎么办?我会考虑更改声明的类型,以便根本不使用class。请注意,这并不意味着JS无法使用class;这只是表示表示这种关系的最佳方法是使用自己的类型和值。例如:

namespace PossibleFix {
  declare namespace Classes {
    interface Parent {
      parentProp: string;
    }
    export const Parent: {
      method(arg1: string): Parent;
      new (): Parent;
    };

    interface Child extends Parent {
      childProp: string;
    }

    export const Child: {
      method(arg1: number, arg2: number): Child;
      new (): Child;
    };
  }

  const child = new Classes.Child();
  const parent = new Classes.Parent();
  const alsoParent: typeof parent = new Classes.Child();

  const childConstructorMethod = Classes.Child.method;
  const parentConstructorMethod = Classes.Parent.method;

  // now there is no implied inheritance on the static side
  const alsoParentConstructorMethod: typeof parentConstructorMethod = childConstructorMethod;
  //    ~~~~~~~~~~~~~~~~~~~~~~~~~~~ <-- error! not assignable

}

这几乎与原始的基于class的类型相同。 class具有实例侧和静态侧。在这里,实例端变为interface,而静态端变为导出的const变量,该变量既是构造函数(new():...),又具有方法。但是在这里,即使实例方继承了(我们仍然有Child extends Parent),静态方现在彼此之间不再相关。这样,您就可以在子构造函数的方法上选择所需的任何签名。

这最终将变得更加冗长,尤其是如果您有正在生成原始声明的实际TypeScript代码,因为现在您必须想出一种方法来断言生成的代码符合后者的声明,并且可以乱七八糟。但是,它给您更多的自由。希望这会有所帮助。祝你好运!

Link to code