Свойство функции против метода

Какие существуют практические различия между определением метода интерфейса:

interface Foo {
    bar(): void;
}

и определяя свойство с типом функции:

interface Foo {
    bar: () => void;
}

?

Ответ 1

Если это единственные объявления, они идентичны.

Единственное отличие состоит в том, что вы можете увеличить первую форму во втором объявлении, чтобы добавить новые подписи:

// Somewhere
interface Foo {
  bar(): void;
}

// Somewhere else
interface Foo {
  bar(s: number): void;
}

// Elsewhere
let x: Foo = ...
x.bar(32); // OK

Ответ 2

Другое отличие состоит в том, что модификатор readonly нельзя применять к методам. Следовательно, следующее назначение не может быть предотвращено:

interface Foo {
    bar(): void;
}

declare var x: Foo;
x.bar = function () { };

Если bar определена как свойство, к нему можно применить модификатор readonly:

interface Foo {
    readonly bar: () => void;
}

предотвращение переназначения.

( { }; interface Foo2 { readonly bar:() => void; } declare var x2: Foo2; x2.bar =() => { }; rel=noreferrer>Детская площадка)

Ответ 3

Кажется, что компилятору это не важно, так как все они действительны:

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() { }
}

( { } } class Foo1Class2 implements Foo1 { bar() { } } interface Foo2 { bar:() => void; } class Foo2Class1 implements Foo2 { bar =() => { } } class Foo2Class2 implements Foo2 { bar() { } } rel=noreferrer>код на детской площадке)

Причина этого, вероятно, связана с тем, как это компилируется в 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 является частью прототипа, который затем может быть переопределен расширяющим классом.

Ответ 4

Наиболее критическое различие заключается в том, что использование машинописного подхода на основе свойств на самом деле противоречиво проверяет типы. Например:

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

Это задокументировано -

Более строгая проверка применяется ко всем типам функций, кроме тех, которые происходят в объявлениях методов или конструкторов. Методы исключены специально, чтобы гарантировать, что универсальные классы и интерфейсы (такие как Array) по-прежнему в основном связаны ковариантно.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html