【发布时间】:2026-01-09 14:40:02
【问题描述】:
定义接口方法有什么实际区别:
interface Foo {
bar(): void;
}
并使用函数类型定义属性:
interface Foo {
bar: () => void;
}
?
【问题讨论】:
标签: typescript
定义接口方法有什么实际区别:
interface Foo {
bar(): void;
}
并使用函数类型定义属性:
interface Foo {
bar: () => void;
}
?
【问题讨论】:
标签: typescript
如果这些是唯一的声明,则它们是相同的。
唯一的区别是您可以在第二个声明中扩充第一个表单以添加新签名:
// Somewhere
interface Foo {
bar(): void;
}
// Somewhere else
interface Foo {
bar(s: number): void;
}
// Elsewhere
let x: Foo = ...
x.bar(32); // OK
【讨论】:
还有另一个区别,readonly 修饰符不能应用于方法。因此,不能阻止以下赋值:
interface Foo {
bar(): void;
}
declare var x: Foo;
x.bar = function () { };
如果bar 被定义为一个属性,那么readonly 修饰符可以应用到它:
interface Foo {
readonly bar: () => void;
}
防止重新分配。
【讨论】:
最关键的区别实际上是使用属性方法 typescript 实际上是对类型进行逆变检查。例如:
type FProp<A> = {
fork: (a: A) => void
}
type FMeth<A> = {
fork(a: A): void
}
type Cat = {
isPurring: boolean
}
type Dog = {
isBarking: boolean
}
const dd = { fork: (a: Cat & Dog) => void 0 }
const fa: FProp<Cat> = dd // will throw up
const fb: FMeth<Cat> = dd // will not issue any error
这是记录在案的——
更严格的检查适用于所有函数类型,除了那些 源自方法或构造函数声明。方法是 专门排除以确保通用类和接口(例如 as Array) 继续主要是协变相关的。
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html
【讨论】:
编译器似乎并不关心,因为所有这些都是有效的:
interface Foo1 {
bar(): void;
}
class Foo1Class1 implements Foo1 {
bar = () => { }
}
class Foo1Class2 implements Foo1 {
bar() { }
}
interface Foo2 {
bar: () => void;
}
class Foo2Class1 implements Foo2 {
bar = () => { }
}
class Foo2Class2 implements Foo2 {
bar() { }
}
原因可能与它如何编译成javascript有关:
var Foo1Class1 = (function () {
function Foo1Class1() {
this.bar = function () { };
}
return Foo1Class1;
}());
var Foo1Class2 = (function () {
function Foo1Class2() {
}
Foo1Class2.prototype.bar = function () { };
return Foo1Class2;
}());
在这两种情况下,其中一个类的实例都会有一个名为 bar 的属性,它是一个可调用函数。
不同之处仅在于Foo1Class2 中的bar 方法是原型的一部分,然后可以被扩展类覆盖。
【讨论】: