TypeScript接口和实现

3

我在尝试使用TypeScript和接口。我有以下代码:

interface Control {
    name: string;
    onSelect():string;
}

class Button {
    name:string="button";
    onSelect = function() {
        return "hello";
    }
}

var a:Button = new Button();
var b:Control = {"name": "arbitrary", "onSelect": () => { return "ahoy!"; }};

var trigger = function(c:Button) {
    console.log(c.name, "says", c.onSelect());
}

trigger(a);
trigger(b);

这段代码编译和运行都没有报错。有人能解释一下为什么我的trigger函数接受b,尽管它期望得到一个类型为Button的参数,而b的类型为Control

即使Button显式实现了Control,我也要求得到一个Button,而不是一个Control。就所有意图而言,Button可能包含其他成员。

是TypeScript根据结构相似性自动推断实现吗?是否允许在期望实现类的地方传递接口?(难道不应该反过来吗?)


1
如果 Button 有一个私有成员,那么只有它本身(或从它继承的类)将被视为兼容类型。如果这是期望的行为,您可以考虑添加一个“虚拟”的私有成员。 - Ryan Cavanaugh
我明白了。我在考虑添加一个虚拟成员。你认为强制执行严格类型是不必要的吗?它是否违反了TS的原则? - Andrei Nemes
我会说这很少是有效的,但总有例外。毕竟,如果trigger实际上依赖于Button的其他方面而不仅仅是它使用的成员,则这有点像设计上的问题。 - Ryan Cavanaugh
感谢您的输入! - Andrei Nemes
2个回答

7
正如在TypeScript接口文档中所描述的那样:

TypeScript的核心原则之一是类型检查关注于值的“形状”。这有时被称为“鸭子类型”。

“鸭子类型”中:

如果它看起来像一只鸭子,游泳像一只鸭子,那么它很可能是一只鸭子。

换句话说,TypeScript检查提供的对象或类型的形状(方法和属性)。如果提供的对象包含由接口描述的所有属性和方法,则该对象很可能被视为与所描述的接口兼容的对象。
在您的示例中,您的接口和类看起来完全相同,因此TypeScript将您的对象视为与所描述的接口兼容的对象。
如果您检查生成的JavaScript,请注意根本没有提到您的接口 - 这是因为TypeScript接口实质上是开发人员提供的类型元数据,有助于TypeScript验证类型兼容性。

有没有办法强制进行严格的类型检查?这会让我更容易忽略可能导致问题的微妙差别。 - Andrei Nemes
@Andrei - 类型安全不是“JavaScript方式™”。许多JS程序员喜欢可以随意使用类型的事实。TS增加了一些类型安全性,但仍然比C/C++/C#/Java等语言更宽松。 - Rich Turner
@Andrie:此外,正如John所提到的那样,如果您使您的对象或接口看起来不同,TypeScript将会提醒您。 - Rich Turner
感谢您的输入! - Andrei Nemes

2
你的接口(interface)和类(class)实现相同,因此当它期望一个按钮(Button)时,你传入了一个兼容对象(对象具有返回字符串的名称和返回字符串的onSelect函数)。如果你添加另一个属性到按钮(Button),例如“greeting: string”,那么在“trigger(b)”上会报错,因为它不是有效的。
interface Control {
    name: string;
    onSelect:() => string; // this is another way to write function that returns a string
}

class Button implements Control {
    name:string="button";
    onSelect = function() {
        return "hello";
    }
    greeting: string; // adding this will cause trigger(b) to fail
}

你知道是否有任何方法可以启用严格的类型检查吗?您认为这是否是不必要的? - Andrei Nemes
@AndreiNemes 我认为这并不是必要的。一旦你的类拥有任何其他类型的属性或者与接口不完全匹配,它就不会被允许。 - John
感谢您的输入! - Andrei Nemes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接