简体   繁体   English

TypeScript 如何为子构造函数使用更严格的类型?

[英]TypeScript how to use stricter types for a child constructor?

Suppose I have two classes, a Dog and an Animal class.假设我有两个类,一个Dog类和一个Animal类。 In this example, Dog extends Animal .在这个例子中, Dog extends Animal Now suppose I have two additional classes (one called Parent , and another called Child ).现在假设我有两个额外的类(一个叫做Parent ,另一个叫做Child )。 In TypeScript, I am able to type the arguments of my child class's constructor function to be that of my parent class's constructor function's arguments using ConstructorParameters like so:在 TypeScript 中,我可以使用ConstructorParameters将子类的构造函数的参数键入为父类的构造函数的参数,如下所示:

class Parent {
  constructor(a: Animal, b: number, c: number) {}
}

class Child extends Parent {
  constructor(...args: ConstructorParameters<typeof Parent>) { // same as parent's arguments: a: Animal, b: number, c: number
    super(...args); // doesn't complain, as args matches the arguments for Parent ([a: Animal, b: number, c: number])
  }
}

In this example, new Child() and new Parent() would accept the same argument types.在此示例中, new Child()new Parent()将接受相同的参数类型。 But how could I type the Child constructor function to accept a stricter type for a , such as Dog ?但是,我怎么能键入子构造函数接受更严格的类型a ,如Dog

class Parent {
  constructor(a: Animal, b: number, c: number) {}
}

class Child extends Parent {
  //          v----- want to access a in `Child`'s constructor
  constructor(a: Dog, ...args: ConstructorParameters<typeof Parent>) {
    super(a, ...args); // <-- complains, I want to pass `a: Dog` and ...args (containing [b: number, c: number])
    a.bark(); // use specific Dog methods (that don't exist on Animal)
  }
}
// Using: 
// - new Child(new Animal(), 0, 0); should complain as Animal is not of type Dog
// - new Child(new Dog(), 0, 0); should work as Dog is of type Dog

The above doesn't work, as Child now expects to be passed a Dog instance followed by the three arguments for the Parent constructor.以上不起作用,因为 Child 现在希望传递一个Dog实例,后跟Parent构造函数的三个参数。 I was thinking of using Omit<> or Partial<> on ConstructorParameters to remove the expected a: Animal type but couldn't seem to get those to work.我正在考虑在ConstructorParameters上使用Omit<>Partial<>来删除预期的a: Animal类型,但似乎无法使它们起作用。 I'm also looking for a solution that doesn't involve me needing to pass through the arguments individually to the super() call (ie: if I add arguments in Parent's constructor, I won't need to add them to the Child).我也在寻找一种解决方案,它不需要我将参数单独传递给super()调用(即:如果我在 Parent 的构造函数中添加参数,则不需要将它们添加到 Child) .

try conditional types尝试条件类型

type RestWithoutOne<K> = K extends [infer WithThis, ...infer WithRest] ? WithRest : never;

class Child extends Parent {
  //          v----- want to access a in `Child`'s constructor
  constructor(a: Dog, ...args: RestWithoutOne<ConstructorParameters<typeof Parent>>) {
    const a = args[0];
    super(a, ...args); // <-- complains, I want to pass `a: Dog` and ...args (containing [b: number, c: number])
    a.bark(); // use specific Dog methods (that don't exist on Animal)
  }
}

Playground link 游乐场链接

But how could I type the Child constructor function to accept a stricter type for a , such as Dog ?但我怎么可以键入Child构造函数接受更严格的类型a ,如Dog

That sounds like a bad idea, violating the Liskov substitution principle : one should be able to use Child everywhere where Parent can be used.这听起来是个坏主意,违反了Liskov 替换原则:应该能够在任何可以使用Parent地方使用Child

I would therefore recommend generics:因此,我会推荐泛型:

class Parent<T extends Animal> {
  constructor(a: T, b: number, c: number) {}
}

class Child extends Parent<Dog> {
  constructor(...args: ConstructorParameters<typeof Parent<Dog>>) {
    super(...args);
    const a = args[0];
    a.bark(); // use specific Dog methods (that don't exist on Animal)
  }
}

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

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