首页 > 解决方案 > 如何通过提供不同的类型来覆盖静态方法

问题描述

我有一个类 ( Foo) 和一个子类 ( ),它们都是具有不同类型Bar extends Foo的静态成员函数 ( )。func(...)

interface IFoo {
  f1: string;
  f2: string;
}

class Foo {
  static func(data: Partial<IFoo>): Partial<IFoo> {
    return { f1: data.f1, f2: data.f2 }
  };
}

interface IBar extends Partial<IFoo>{
  b1: string;
  b2: string;
}

class Bar extends Foo {
  static func(data: Partial<IBar>): Partial<IBar> {
    return { ...Foo.func(data), b1: data.b1, b2: data.b2 }
  };
}

在这个例子中,一切似乎都很好,但我需要不带类型修饰符的func静态方法的第二个版本:Partial

interface IFoo {
  f1: string
  f2: string
}

class Foo {
  static func(data: Partial<IFoo>): Partial<IFoo> {
    return { f1: data.f1, f2: data.f2 }
  };

  static func2 = Foo.func as (data: IFoo) => IFoo; // This one
}

interface IBar extends Partial<IFoo>{
  b1: string;
  b2: string;
}

class Bar extends Foo {
  static func(data: Partial<IBar>): Partial<IBar> {
    return { ...Foo.func(data), b1: data.b1, b2: data.b2 }
  };

  static func2 = Bar.func as (data: IBar) => IBar; // This one
}

错误......func并且func2在技术上做同样的事情,除了func允许有不完整的对象参数,相反func2需要一个完全填充的参数。

但是我在“Bar”类上遇到了这个错误:

Class static side 'typeof Bar' incorrectly extends base class static side 'typeof Foo'.
  Types of property 'func2' are incompatible.
    Type '(data: IBar) => IBar' is not assignable to type '(data: IFoo) => IFoo'.
      Types of parameters 'data' and 'data' are incompatible.
        Type 'IFoo' is missing the following properties from type 'IBar': b1, b2ts(2417)

由于funcfunc2是相同的方法,但只是带有Partial修饰符,有没有办法摆脱这个错误并能够func2正确覆盖?

=====

编辑 :

@jcalz 解决方案都很好,但如果Foo是抽象类,我收到以下错误:

Type 'typeof Foo' is not assignable to type '(new () => Foo) & Pick<typeof Foo, "prototype" | "func">'.
  Type 'typeof Foo' is not assignable to type 'new () => Foo'.
    Cannot assign an abstract constructor type to a non-abstract constructor type.ts(2322)

代码 :

const FooWithoutFunc2: (new () => Foo) & Omit<typeof Foo, "func2"> = Foo;

class Bar extends FooWithoutFunc2 {
  // ...
}

我想我不能输入 Foo ,(new () => Foo)因为它不是新的......我怎么能输入这样的摘要FooWithoutFunc2

谢谢你。

标签: typescript

解决方案


如果你覆盖某些东西,它需要与你覆盖的东西保持兼容。在 TypeScript 中,这是在类的静态端以及类的实例端强制执行的。这意味着某人应该能够以Bar.func2()与他们可以调用相同的方式进行调用Foo.func2()。但是你不能Bar.func2({f1: "", f2: ""})在你的定义中调用,所以 static 的一面Bar没有正确地扩展Foo.

因此,可能的修复:


Bar.func2({f1: "", f2: ""})如果有人打电话回来,也许没关系{f1: "", f2: "", b1: undefined, b2: undefined}。如果我要求Foo.func2,而你递给我Bar.func2,并且我像调用一样调用它Foo.func2,我会得到一个带有这些未定义的额外属性的输出,但结果仍然是有效的IFoo,所以我应该很高兴(或者至少它遵守签名)。这意味着Bar.func2既是有效(x: IBar)=>IBar的又是有效的(x: IFoo)=>IFoo。因此,我们可以使用交集类型将其声明为两者,这相当于说Bar.func2具有两个重载的调用签名:

  class Bar extends Foo {
    static func(data: Partial<IBar>): Partial<IBar> {
      return { ...Foo.func(data), b1: data.b1, b2: data.b2 };
    }
    static func2 = Bar.func as ((data: IBar) => IBar) & typeof Foo.func2; // okay
  }

  Foo.func2({ f1: "", f2: "" });
  Bar.func2({ f1: "", f2: "", b1: "", b2: "" });
  Bar.func2({ f1: "", f2: "" }); // also okay because Bar.func2 extends Foo.func2
  const foo: Foo = new Bar(); // but Bar still extends Foo (the instance side)

或者,也许你真的不希望任何人Bar.func2()用不是IBar参数的东西来调用。在那种情况下,这不可能是真的class Bar extends Foo。的实例侧Bar扩展了 的实例侧Foo,但构造函数对象本身没有。我们也可以通过创建另一个构造函数对象来表达这一点,该对象Foo生成实例但编译器不知道它具有func2静态方法:

  const FooWithoutFunc2: (new () => Foo) & Omit<typeof Foo, "func2"> = Foo;
  class Bar extends FooWithoutFunc2 {
    static func(data: Partial<IBar>): Partial<IBar> {
      return { ...Foo.func(data), b1: data.b1, b2: data.b2 };
    }
    static func2 = Bar.func as ((data: IBar) => IBar); // okay
  }

  Foo.func2({ f1: "", f2: "" });
  Bar.func2({ f1: "", f2: "", b1: "", b2: "" });
  Bar.func2({ f1: "", f2: "" }); // error, typeof Bar doesn't extend typeof Foo anymore
  const foo: Foo = new Bar(); // but Bar still extends Foo (the instance side)

现在Bar仍然Foo在实例端扩展,但在静态端 Bar扩展。FooWithoutFunc2


我想这取决于您的用例是否适合您。无论如何,希望有所帮助;祝你好运!

链接到代码


推荐阅读