从另一个文件中导出一个 TypeScript 类,而不是在其中定义的

Loi*_*ilo 4 module typescript

有一段时间我一直试图从我在一个地方编写的一个小库中导出类。我希望每个班级都不知道它们是如何提供给外部的,但到目前为止我无法让它发挥作用。

让我们假设我有课 Foo

// lib/foo.ts
export class Foo {}
Run Code Online (Sandbox Code Playgroud)

和班级 Bar

// lib/bar.ts
export class Bar {}
Run Code Online (Sandbox Code Playgroud)

现在我想让那些通过MyModule命名空间使用包的人可以使用它们,这就是它变得棘手的地方。我不知道如何从定义在其他地方而不是类本身的命名空间中导出现有类。

显然,这不起作用,因为它不是有效的 TypeScript:

// api.ts
import { Foo } from "./lib/foo";
import { Bar } from "./lib/bar";

export namespace MyModule {
    export Foo;

    export namespace MySubModule {
        export Bar;
    }
}
Run Code Online (Sandbox Code Playgroud)

我找到了两种可能的解决方案,但两者都有其缺点。我可以定义一个变量并将类分配给它:

// api.ts
import { Foo as MyFoo } from "./lib/foo";
import { Bar as MyBar } from "./lib/bar";

export namespace MyModule {
    export const Foo = MyFoo;

    export namespace MySubModule {
        export const Bar = MyBar;
    }
}
Run Code Online (Sandbox Code Playgroud)

这实际上将允许我从外部位置创建类的实例,new MyModule.MySubModule.Bar但不会让我有可能将它们用作类型,因为

let myBarInstance: MyModule.MySubModule.Bar;
Run Code Online (Sandbox Code Playgroud)

会抛出一个 TypeScript 错误,指出

模块“path/to/module/api”。MySubModule 没有导出成员“Bar”

export type Bar = MyBar相反,我尝试使用代替,const而这在说时有效

let myBarInstance: MyModule.MySubModule.Bar
Run Code Online (Sandbox Code Playgroud)

另一方面,我自然无法实例化,MyModule.MySubModule.Bar因为它只是一个类型别名。

所有这些对于Foo班级来说都是一样的。

有没有人有这方面的经验,我是否可能忽略了 TypeScript 提供的其他一些导出功能?

Loi*_*ilo 7

所以来自 TypeScript 团队的 Ryan Cavanaugh 非常友好地在他们的 GitHub 上回答了我的问题

这实际上很简单:只需结合我提出的两种方法,您就可以开始了:

// api.ts
import { Foo as MyFoo } from "./lib/foo";
import { Bar as MyBar } from "./lib/bar";

export namespace MyModule {
    export const Foo = MyFoo;
    export type Foo = MyFoo;

    export namespace MySubModule {
        export const Bar = MyBar;
        export type Bar = MyBar;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑 2020:既然我有更多年的 TypeScript 经验,我想我应该提供一个额外的解释为什么上面的代码有效。这对于新手 TypeScript 开发人员来说可能是有价值的信息,因为它涉及一个不常教授的主题:TypeScript 的独立类型范围。

TypeScript 基本上维护一个与 JavaScript 的变量范围平行的类型范围。这意味着您可以在同一个文件中定义一个变量foo和一个类型foo。它们甚至不需要兼容:

const foo = 'bar'
type foo = number
Run Code Online (Sandbox Code Playgroud)

现在 TypeScript 中的类有点特殊。如果你定义一个类Foo,TypeScript 不仅会创建一个变量Foo(包含类对象本身),还会声明一个 typeFoo,表示该类的一个实例。

类似地,当从另一个文件导入名称时,该名称下的任何已定义变量和类型都会被导入。这意味着,在上述代码中,MyFoo保持Foo类对象以及Foo从类型foo.ts

因此,如果我们通过写入MyFooMyModule命名空间内部重新导出export const Foo = MyFoo,则只会导出类对象(因为 aconst只包含值,而不是类型)。同样,如果我们这样做export type Foo = MyFoo,则只会导出类类型

所以现在我们又回到了原点:由于独立的作用域,以相同的名称导出值和类型是有效的。在某些情况下(例如这种情况),它不仅有效而且是必要的。