is/are 在 Typescript 中导出接口的目的是什么?

What is/are the purpose(s) in exporting an interface in Typescript?

Typescript 新手。我可以看到您可以定义一个接口并在整个 Typescript 项目中使用它。我可以看到许多描述如何 "export" 界面的博客、教程和文档。但是,我不明白你为什么要这样做。我当然可以理解导出 类 或常量或变量....但是导出接口有什么好处呢?好像需要的时候可以随时参考使用?

显然我遗漏了一个关键概念,非常感谢您提供的任何清晰或见解。

编译安全

您可以导出一个接口,以便可以将其导入另一个 class,后者将实现它。这样你就可以获得围绕接口契约的类型安全。

假设您有一个界面 IPerson(抱歉陈词滥调!)

export interface IPerson {
    getFirstName(): string;
    getLastName(): string;
    getAgeInYears(): number;
}

现在您对该接口进行了具体实现,称为 Person,但实际上并未引用该接口:

export class Person {
    private _firstName: string;
    private _lastName: string;
    private _ageInYears: number;

    constructor (firstName: string, lastName: string, ageInYears: number) {
        this._firstName = firstName;
        this._lastName = lastName;
        this._ageInYears = ageInYears;
    }

    public getFirstName(): string {
        return this._firstName;
    }

    public getLastName(): string {
        return this._lastName;
    }

    public getAgeInYears(): number{
        return this._ageInYears;
    }
}

您的 class 将正常运行,并且具有 IPerson 的所有预期行为,但是如果您随后向 IPerson 接口添加另一个方法(例如 getFirstPetName(): string),您的项目仍将继续构建(这可能会导致意外行为)。

这可以通过显式导入接口并使 Person 实现它来解决,这样除非 Person 符合合同,否则您的项目将无法构建。

import { IPerson } from "./person.i";

export class Person implements IPerson {
    // ...
}

依赖抽象

通过导出一个接口,它可以被其他地方导入并依赖,而不是你的程序依赖具体的实现。 (这是dependency inversion principle

如果你有一个函数 getPersonFullName 接受一个 Person:

getPersonFullName(person: Person) {
    return person.getFirstName() + " " + person.getLastName();
}

这将无法用于 IPerson 的其他实现,即使它们满足相同的合同和要求(例如,如果您制作了 IFictionalPerson)。

通过导入 IPerson 并依赖它,您可以确保您的函数将适用于 IPerson 的任何实现,而不是特定的 Person 实现。

import { IPerson } from "./person.i";

getPersonFullName(person: IPerson) {
    return person.getFirstName() + " " + person.getLastName();
}

您可能想要导出它,以便您可以使用它来模拟某种数据格式,这样您就可以在 IDE 中自动完成并在编译期间进行类型检查。

我自己在不需要完全成熟的情况下就这样做了 class 所以我使用接口来描述数据对象的外观,这样我在开发过程中就可以减少猜测。接口未编译成 Javascript 并丢失。它们仅在编译期间或 IDE 开发期间有用。

It seems like you can always use it by reference if necessary?

全球接口无处不在。要制作全局接口,只需将代码放在没有顶级 importexport 关键字的 TypeScript 文件中。

但是,就像老式的全局变量一样,全局接口也存在命名冲突。导出的接口解决了这个问题。

示例:

// a.ts
export interface A {}
export default let a: A;

// b.ts
import b, {A as B} from './a'
// Here, the name of A is B