在 TypeScript 中键入由继承方法调用的重写方法(错误?)
Typing of overridden method, called by inherited method, in TypeScript (Bug?)
我最近偶然发现了以下场景:
class A {
public m1(x: string | string[]): string | string[] {
return this.m2(x);
}
protected m2(x: string | string[]): string | string[] {
return x;
}
}
class B extends A {
protected m2(x: string[]): string { // no compiler warning
return x.join(',');
}
}
const b = new B();
console.log(b.m1(['a', 'b', 'c'])); // ok
console.log(b.m1('d')); // runtime error
这是 TypeScript 打字系统的错误还是故意的?如果是后者,我如何更改类型以便编译器识别问题?
这是预期的行为。在打字稿中,您重写一个方法来降低方法的特异性,而不是像在其他语言中那样扩大它。
至于说明...
class A {
public m1(x: string | string[]): string | string[] {
/**
* Here variable 'x' is of type string | string[], which perfectly overlaps
* with the arguments for the method m2. So you won't get a compile-time error
*/
return this.m2(x);
}
protected m2(x: string | string[]): string | string[] {
return x;
}
}
class B extends A {
/**
* Here method m2 is overridden by a method which accepts a string[], which
* is covered by the parent's method, and hence no compile-time error. If
* you made the argument type to 'string', it would still be accepted.
*/
protected m2(x: string[]): string { // no compiler warning
return x.join(',');
}
}
const b = new B();
console.log(b.m1(['a', 'b', 'c'])); // ok
/**
* Here you are passing the type you have specified method m1 will take. So there is no issue here as well
*/
console.log(b.m1('d')); // runtime error
综上所述,这是一个 logical-error,而不是一个错误。
在 TypeScript 中,方法是 bivariant and function properties contravariant with --strictFunctionTypes
。注意它们不同的语法:
class MyClass {
public fn(a: string): string { ... } // this is a method
public fn = (a: string): string => { ... } // this is a function as property
}
要为 m2
启用更严格的输入,您需要启用 --strictFunctionTypes
(默认为 strict
)并使用函数 属性:
protected m2 = (x: string | string[]): string | string[] => {
return x;
}
现在,m2
会正确出错,您需要区分 string
和 string[]
:
protected m2 = (x: string | string[]): string => {
return Array.isArray(x) ? x.join(',') : `${x},`;
}
Live code example on Playground
我最近偶然发现了以下场景:
class A {
public m1(x: string | string[]): string | string[] {
return this.m2(x);
}
protected m2(x: string | string[]): string | string[] {
return x;
}
}
class B extends A {
protected m2(x: string[]): string { // no compiler warning
return x.join(',');
}
}
const b = new B();
console.log(b.m1(['a', 'b', 'c'])); // ok
console.log(b.m1('d')); // runtime error
这是 TypeScript 打字系统的错误还是故意的?如果是后者,我如何更改类型以便编译器识别问题?
这是预期的行为。在打字稿中,您重写一个方法来降低方法的特异性,而不是像在其他语言中那样扩大它。
至于说明...
class A {
public m1(x: string | string[]): string | string[] {
/**
* Here variable 'x' is of type string | string[], which perfectly overlaps
* with the arguments for the method m2. So you won't get a compile-time error
*/
return this.m2(x);
}
protected m2(x: string | string[]): string | string[] {
return x;
}
}
class B extends A {
/**
* Here method m2 is overridden by a method which accepts a string[], which
* is covered by the parent's method, and hence no compile-time error. If
* you made the argument type to 'string', it would still be accepted.
*/
protected m2(x: string[]): string { // no compiler warning
return x.join(',');
}
}
const b = new B();
console.log(b.m1(['a', 'b', 'c'])); // ok
/**
* Here you are passing the type you have specified method m1 will take. So there is no issue here as well
*/
console.log(b.m1('d')); // runtime error
综上所述,这是一个 logical-error,而不是一个错误。
在 TypeScript 中,方法是 bivariant and function properties contravariant with --strictFunctionTypes
。注意它们不同的语法:
class MyClass {
public fn(a: string): string { ... } // this is a method
public fn = (a: string): string => { ... } // this is a function as property
}
要为 m2
启用更严格的输入,您需要启用 --strictFunctionTypes
(默认为 strict
)并使用函数 属性:
protected m2 = (x: string | string[]): string | string[] => {
return x;
}
现在,m2
会正确出错,您需要区分 string
和 string[]
:
protected m2 = (x: string | string[]): string => {
return Array.isArray(x) ? x.join(',') : `${x},`;
}
Live code example on Playground