【问题标题】:Function property vs method函数属性与方法
【发布时间】:2026-01-09 14:40:02
【问题描述】:

定义接口方法有什么实际区别:

interface Foo {
    bar(): void;
}

并使用函数类型定义属性:

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

?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    如果这些是唯一的声明,则它们是相同的。

    唯一的区别是您可以在第二个声明中扩充第一个表单以添加新签名:

    // Somewhere
    interface Foo {
      bar(): void;
    }
    
    // Somewhere else
    interface Foo {
      bar(s: number): void;
    }
    
    // Elsewhere
    let x: Foo = ...
    x.bar(32); // OK
    

    【讨论】:

    • 这不是唯一的区别。请参阅 tusharmath 和 Zev Spitz 的答案。
    【解决方案2】:

    还有另一个区别,readonly 修饰符不能应用于方法。因此,不能阻止以下赋值:

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

    如果bar 被定义为一个属性,那么readonly 修饰符可以应用到它:

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

    防止重新分配。

    (Playground)

    【讨论】:

    • 很好的观察和对问题的补充。实际上非常有用的知识。
    【解决方案3】:

    最关键的区别实际上是使用属性方法 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

    【讨论】:

      【解决方案4】:

      编译器似乎并不关心,因为所有这些都是有效的:

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

      (code in playground)

      原因可能与它如何编译成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 方法是原型的一部分,然后可以被扩展类覆盖。

      【讨论】:

        最近更新 更多