TypeScript 版本: 2.6.2
问题
初始场景
昨天我尝试给一个接口继承链添加一些功能时遇到了这个问题。这个问题的背景是想象出来的,比实际需要展示的问题要长,但我想提供一个完整的示例来清楚地表明需要解决这个问题。
所以,让我们想象一个测试应用程序,它应该对设备进行仪器化:以下是一个潜在的情况模型:
interface Stream
{
}
interface InputStream
{
}
interface OutputStream
{
}
// interface inheraitance is a great feature
interface IOStream extends InputStream, OutputStream
{
}
interface InterfaceLike
{
}
interface DeviceInterfaceLike extends InterfaceLike
{
init();
}
interface InputDeviceInterfaceLike extends DeviceInterfaceLike
{
input(); // this should word just like the `touch` command
// like if there's and error with the device, it will trigger exception
input(stream: InputStream);
}
interface ScreenDeviceInterfaceLike extends InputDeviceInterfaceLike
{
input(stream: InputStream, coordinates: {x: number, y: number});
}
interface TouchScreenDeviceInterfaceLike extends InputDeviceInterfaceLike
{
input(stream: IOStream, coordinates: {x: number, y: number});
}
预期行为:
它应该只是在子接口中重载那个方法。
实际行为:
error TS2430: Interface 'ScreenDeviceInterfaceLike' incorrectly extends interface 'InputDeviceInterfaceLike'.
Types of property 'input' are incompatible.
Type '(stream: InputStream, coordinates: { x: number; y: number; }) => any' is not assignable to type '{ (): any; (stream: InputStream): any; }'.
core.ts(73,11): error TS2430: Interface 'TouchScreenDeviceInterfaceLike' incorrectly extends interface 'InputDeviceInterfaceLike'.
Types of property 'input' are incompatible.
Type '(stream: IOStream, coordinates: { x: number; y: number; }) => any' is not assignable to type '{ (): any; (stream: InputStream): any; }'.
18:38:26 - Compilation complete. Watching for file changes.
它要求我在每个接口中完全复制粘贴所有方法签名。
interface ScreenDeviceInterfaceLike extends InputDeviceInterfaceLike
{
input(); // this should word just like the `touch` command
// like if there's and error with the device, it will trigger exception
input(stream: InputStream);
input(stream: InputStream, coordinates: {x: number, y: number});
}
interface TouchScreenDeviceInterfaceLike extends InputDeviceInterfaceLike
{
input(); // this should word just like the `touch` command
// like if there's and error with the device, it will trigger exception
input(stream: InputStream);
input(stream: IOStream, coordinates: {x: number, y: number});
}
当你可以通过继承链分布多达(为什么不呢)15个重载时... 它变得(是的,你说对了)庞大。
现在,编译器还不能确定何时 override
继承自父类的方法签名,何时 overload
它们。
建议
我的第一个想法是引入一个新的关键字,就像 @ts-nocheck
和 @override
一样。
TouchScreenDeviceInterfaceLike
然后,@overload
接口将变成:
interface TouchScreenDeviceInterfaceLike extends InputDeviceInterfaceLike
{
@override // it is now clear that the only way to instrument a touchscreen is to provide a `IOStream` and coordinates (`{x: number, y: number}`)
input(stream: IOStream, coordinates: {x: number, y: number});
}
此外, @override
当默认行为是重载时是有用的,但我们仍然可以定义默认行为以 在子接口中重定义方法时抑制所有父类定义,除非使用 @overload
。也就是说
interface InputDeviceInterfaceLike extends DeviceInterfaceLike
{
// to override all definitions from parents
init(istream: InputStream);
}
或者
interface InputDeviceInterfaceLike extends DeviceInterfaceLike
{
@overload // to overload signatures from parents
init(istream: InputStream);
}
而 TouchScreenDeviceInterfaceLike
接口可以变成:
interface TouchScreenDeviceInterfaceLike extends InputDeviceInterfaceLike
{
@overload // to overload signatures from parents
{
init(istream: InputStream); // except this signature... So that it remains only the empty parameter and the below
}
input(stream: IOStream, coordinates: {x: number, y: number});
}
通过仔细思考,我认为第一步应该是允许继承类型默认重载方法。
= = = = = = 更新 = = = = = =
我现在认为有必要补充一点,绝对不是通过 @Override
在接口继承链的任何地方完全擦除(删除、移除)一个方法。... 甚至不通过 @Override
!因为这可能会(将会)破坏OOP继承的核心概念。这个机制是为了确保只在给定的继承节点处理(接受)某些方法(签名)。换句话说,如果 @Override
抑制了所有签名,那么至少必须有一个方法签名。
7条答案
按热度按时间ycggw6v21#
这个问题也适用于被重写的枚举。
这目前会导致以下错误:
但从语言的Angular 来看,能够在派生类型中重写枚举是一个完全合法的期望。
vbkedwbf2#
但是从语言的Angular 来看,能够在派生类型中覆盖枚举是完全合法的期望。这是一个正确的错误。
DerivedType
不是BaseType
,因为通过BaseType
引用查看DerivedType
的代码不期望看到值CAW
。kiayqfof3#
@RyanCavanaugh实际上,经过深思熟虑,我同意你的观点。我自己对此感到矛盾,但还是写下了这些。
s3fp2yjn4#
我现在认为有必要补充一点,绝对不是说通过接口继承链,一个方法就应该完全被擦除(删除、移除)。甚至连
@Override
都不能这么做!因为这可能会(将)破坏OOP继承的核心概念。这个机制的目的是确保在给定的继承节点上只处理(接受)某些方法的签名。换句话说,如果
@Override
抑制了所有签名,那么至少需要一个方法签名。t40tm48m5#
当我仔细思考时,我意识到这将与一个名为
Override
的终端用户装饰器冲突。尽管any
、keyof
和其他一些字符串被保留为TypeScript关键字...我建议一个替代方案。@!Override
或者只是!Override
(为什么不呢)这不会产生冲突,因为它目前是一个语法错误。
nue99wik6#
我有一些类似于以下的React属性:
IValidationTextAreaProps extends IAutoSizeTextAreaProps extends HTMLProperties<HTMLTextArea>
使用三个不同的名称来表示
onChange
监听器真的很烦人。HTMLTextarea
有onChange: (event) => boolean
AutoSizeTextArea
有onValueChange: (newVal) => void
而我不得不想出另一个人为的名称来表示ValidationTextAreaProps...可能是
valueChangeListener: (newVal, isValid) => void
希望没有人会尝试使用
onChange
或onValueChange
。建议:
完全从TypeScript编译器中移除它,只将其作为linting错误
或者,如果这看起来太疯狂了,这些可能会起作用:
或者
任何事情都可以,只要TypeScript不会阻止这个JavaScript功能。
fzwojiic7#
也许我们可以使用自定义类型来解决这个问题:
我发现使用内置类型的方法更简单:
以下是示例用法:
从截图来看,一切正常: