如何使用其他类型来覆盖静态方法

时间:2019-07-25 23:28:42

标签: typescript

我有一个类(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
}

Err ... funcfunc2在技术上做同样的事情,除了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修饰符,有什么方法可以消除此错误并能够覆盖{{1} }正确吗?

=====

编辑:

@jcalz解决方案都很好,但是如果func2是一个抽象类,则会出现以下错误:

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,因为它不是 newable ...。如何键入这样的抽象(new () => Foo)

谢谢。

1 个答案:

答案 0 :(得分:1)

如果您覆盖某个东西,它需要与您覆盖的东西保持兼容。在TypeScript中,这是在类的静态方面以及类的实例方面实施的。这意味着某人应该能够像呼叫Bar.func2()一样呼叫Foo.func2()。但是您不能在定义中调用Bar.func2({f1: "", f2: ""}),因此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。因此,我们可以使用intersection type声明它们都是它们,这相当于说Bar.func2有两个overloaded呼叫签名:

  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


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

Link to code