Какие существуют практические различия между определением метода интерфейса:
interface Foo {
bar(): void;
}
и определяя свойство с типом функции:
interface Foo {
bar: () => void;
}
?
Какие существуют практические различия между определением метода интерфейса:
interface Foo {
bar(): void;
}
и определяя свойство с типом функции:
interface Foo {
bar: () => void;
}
?
Если это единственные объявления, они идентичны.
Единственное отличие состоит в том, что вы можете увеличить первую форму во втором объявлении, чтобы добавить новые подписи:
// 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;
}
предотвращение переназначения.
Кажется, что компилятору это не важно, так как все они действительны:
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
является частью прототипа, который затем может быть переопределен расширяющим классом.
Наиболее критическое различие заключается в том, что использование машинописного подхода на основе свойств на самом деле противоречиво проверяет типы. Например:
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