在类外部和内部定义接口有什么区别?

问题描述 投票:0回答:1

根据我的理解,这就是在类中定义接口的方式:

class A {
  a: number
  constructor() {
    this.a = 0
  }
}

这就是在类外定义接口的方式:

interface A {
  a: number
}
class A {
  constructor() {
    this.a = 0
  }
}

这两种情况有什么区别?我什么时候应该使用其中一种而不是另一种?我注意到

private
protected
修饰符不能在界面中使用,所以如果我需要使用它们,那么我当然需要使用内部案例。但如果我只使用
public
,那么它们之间似乎没有什么区别?

阅读ClassesEveryday Types的文档没有帮助。

typescript class interface
1个回答
0
投票

您所说的“在类中定义接口”只是编写类的正常方式。 Fields一般在类中声明:

class A {
  a: number
  constructor() {
    this.a = 0
  }
}

如果您编译上述内容以针对现代 JavaScript 并使用适当的编译器选项,它将生成一个带有 public class 字段:

的 JavaScript 类
// JavaScript
class A {
    a; // <-- public class field declaration
    constructor() {
        this.a = 0;
    }
}

另一方面,您所说的“在类外部定义接口”被称为声明合并。接口与类实例类型合并,这在类型级别对其进行了“修补”。接口声明绝对没有运行时效果,因此通常放入类声明中的任何内容都可能表现不同。例如:

interface B {
  a: number
}
class B {
  constructor() {
    this.a = 0
  }
}

如果使用与上面相同的编译器设置进行编译,您将获得以下 JavaScript,没有类字段声明:

// JavaScript
class B {
    // <-- no public class field declaration
    constructor() {
        this.a = 0;
    }
}

这两种方法可能会产生不同的 JavaScript,这一事实表明它们并不相同,尽管在此示例中几乎没有实际差异。不过,您可以轻松地更改存在明显差异的内容:

class C { a?: number }
console.log(Object.keys(new C()).length) // 1

interface D { a?: number }
class D { }
console.log(Object.keys(new D()).length) // 0

当然存在类型检查差异。接口合并添加内容而无需通过正常类型的类型检查:

class E { a: number } // compiler error
//        ~
// Property 'a' has no initializer and is not definitely 
// assigned in the constructor.
new E().a.toFixed(); // runtime error

interface F { a: number }
class F { } // no compiler error
new F().a.toFixed(); // runtime error

在上面,

E
给你一个编译器错误,你忘记初始化
a
,而
F
不会给你这个错误,即使存在同样的问题。因此
E
会警告您
F
没有注意到的问题。


此外,接口不是类声明,因此许多通常可以执行的

class
特定操作在接口合并中不可用。 最明显的例子是类允许您编写运行时代码,而接口则不允许。您可以初始化类字段并实现类方法,但是“初始化”接口属性或“实现”接口方法没有多大意义:

class G { a: number = 1; b(): number { return 2 } };

interface H { a: number = 1, b(): number { return 2 }} // errors!

任何特定于类的修饰符,例如

private
protected
static
abstract
在界面中不可用:

abstract class I {
  abstract a: number;
  private b?: number;
  static c: number;
}

interface J {
  abstract a: number; // error!
  private b?: number; // error!
  static c: number; // error!
}

至于你应该做什么,这至少有些主观。在我看来,声明合并是一种高级技术,仅用于解决现有代码中的限制。如果你可以在不使用它的情况下做某事,你应该这样做。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.