TypeScript 如何为 child 构造函数使用更严格的类型?
TypeScript how to use stricter types for a child constructor?
假设我有两个 class,一个 Dog
和一个 Animal
class。在这个例子中,Dog extends Animal
。
现在假设我有两个额外的 classes(一个叫 Parent
,另一个叫 Child
)。在 TypeScript 中,我可以使用 [=18] 将 child class 的构造函数的参数键入为 parent class 的构造函数的参数=] 像这样:
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])
}
}
在此示例中,new Child()
和 new Parent()
将接受相同的参数类型。但是我如何输入 Child 构造函数来接受更严格的 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
以上不起作用,因为 Child 现在希望传递一个 Dog
实例,后跟 Parent
构造函数的三个参数。我在考虑使用 Omit<>
或 ConstructorParameters
上的 Partial<>
以删除预期的 a: Animal
类型,但似乎无法使它们正常工作。我也在寻找一种解决方案,不需要我将参数单独传递给 super()
调用(即:如果我在 Parent 的构造函数中添加参数,我不会需要将它们添加到 Child).
尝试条件类型
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)
}
}
But how could I type the Child
constructor function to accept a stricter type for a
, such as Dog
?
这听起来像是个坏主意,违反了 Liskov substitution principle:在可以使用 Parent
的任何地方都应该可以使用 Child
。
因此我会推荐仿制药:
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)
}
}
假设我有两个 class,一个 Dog
和一个 Animal
class。在这个例子中,Dog extends Animal
。
现在假设我有两个额外的 classes(一个叫 Parent
,另一个叫 Child
)。在 TypeScript 中,我可以使用 [=18] 将 child class 的构造函数的参数键入为 parent class 的构造函数的参数=] 像这样:
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])
}
}
在此示例中,new Child()
和 new Parent()
将接受相同的参数类型。但是我如何输入 Child 构造函数来接受更严格的 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
以上不起作用,因为 Child 现在希望传递一个 Dog
实例,后跟 Parent
构造函数的三个参数。我在考虑使用 Omit<>
或 ConstructorParameters
上的 Partial<>
以删除预期的 a: Animal
类型,但似乎无法使它们正常工作。我也在寻找一种解决方案,不需要我将参数单独传递给 super()
调用(即:如果我在 Parent 的构造函数中添加参数,我不会需要将它们添加到 Child).
尝试条件类型
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)
}
}
But how could I type the
Child
constructor function to accept a stricter type fora
, such asDog
?
这听起来像是个坏主意,违反了 Liskov substitution principle:在可以使用 Parent
的任何地方都应该可以使用 Child
。
因此我会推荐仿制药:
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)
}
}