从使用命名空间的外部typescript库导入类

时间:2017-12-05 14:15:13

标签: angular typescript namespaces

我有一个现有的Angular 2项目,现在我必须使用一个使用命名空间的外部TypeScript库。我不知何故需要从外部库导入一些类,以便我可以使用它们。

externalLibrary.ts

namespace com.foo.bar {

  export class A {
    doSomething() {
    }
  }
}

在我想要做的My Angular2组件中:

myA: A = new A();
myA.doSomething(); 

到目前为止,我已经尝试过:

1)

import {A} from '../dir/externalLibrary';

导致编译错误,因为它说:'externalLibrary.ts不是模块'。

2)

/// <reference path='../dir/externalLibrary.ts'>
...
myA: com.foo.bar.A = new com.foo.bar.A();

导致运行时错误说:'ReferenceError:com未定义'

我的问题是:如何在我的项目中使用A类? 我感谢任何帮助。

编辑1:

只是为了澄清:外部库是一个Java库,然后将其转换为TypeScript。它由大约80,000行代码组成,因此我不能改变太多,因为它需要花费太多时间。这也是我必须坚持命名空间的原因,因为Typescipt转换器从现有的java包结构中创建它们。

@Aaron:感谢您的回答,但我即将出现的'导出命名空间'方法问题如下: 如果我在命名空间之前放置'export',则不能再找到其他类:

b.ts:

export namespace com.foo.bar 
{
    export interface B {

    }
}

a.ts

export namespace com.foo.bar {

    export class A implements B
    {

    }
}

在文件a.ts中,编译器说:'找不到名字B'。 我也试图以某种方式导入B但我失败了。导入B是否有任何可能性,或者我完全是错误的方式?

2 个答案:

答案 0 :(得分:2)

  

导致编译错误,因为它说:&#39; externalLibrary.ts不是模块&#39;。

这个编译错误似乎是事实。看看你的定义它不是导出模块,它只是声明一个全局命名空间。您可以使用import alias(TS功能,而不是ES6)来清理您的使用情况:

import A = com.foo.bar.A;
new A();
/// <reference path='../dir/externalLibrary.ts'>`
myA: com.foo.bar.A = new com.foo.bar.A();
     

导致运行时错误说:&#39; ReferenceError:com未定义&#39;

这意味着您需要在运行的代码中实际包含已编译的externalLibrary.js。这可能就像<script>标记一样简单,或者如果您使用的是可能能够将文件作为副作用导入的文件夹:import "./dir/externalLibrary";

如果您控制外部库(如果您这样做,我不清楚),那么您可以导出命名空间:

export namespace com.foo.bar { ... }

将其转换为模块(导出命名空间),您可以导入顶级命名空间:

import { com } from "../dir/externalLibrary";
const a: com.foo.bar.A = new com.foo.bar.A();

您无法直接导入A,但仍可以使用导入别名:

import { com } from "../dir/externalLibrary";
import A = com.foo.bar.A;
const a: A = new A();

但此时我会说the namespace is pointless。只需从您的模块中导出该类:

export class A { ... }

现在您可以直接导入并使用它:

import { A } from "../dir/externalLibrary";
const a = new A();
a.doSomething();

修改

将文件转换为模块后(通过添加顶级export),只有导出的成员在文件外可见,并且必须由其他文件显式导入才能使用。

您已使用此前的示例更新了您的问题:

// a.ts
namespace com.foo.bar {
    export class A implements B { }
}
// b.ts
namespace com.foo.bar {
   export class B { }
}

在这种情况下,它正在做的是在全局范围内合并名称空间com.foo.bar。但是,一旦你创建了这些文件模块(通过添加export namespace),它们就不再在全局范围内,除非它们明确地相互导入,否则它们彼此之间并不知道,所以上面的A implements B并没有。 ;不知道B是什么了。

您需要导入B。但是使用模块,您无法将导入的名称与本地声明的名称合并,因此我之前导入{com}的示例不会起作用(当您将模块文件视为独立时,它也没有意义范围,它们是什么):

// a.ts
import { com } from "./b"; // import "com"
export namespace com.foo.bar { } // declare "com" -- Error: name conflict

您可以使用* as通配符(基本上是ES6名称空间导入)导入其他文件以避免此冲突:

// a.ts
import * as b from "./b";
import B = b.com.foo.bar.B; // alias now starts with `b.`
export namespace com.foo.bar {
    export class A implements B { }
}

我现在要说的是,此代码生成的格式已过时并导致与ES6模块的摩擦。在ES6模块中使用命名空间是considered mostly anachronistic - 在ES6模块文件存在之前,它们自己定义范围,命名空间(在ES5中只是全局对象/函数闭包)是一种定义范围的方法。使用ES6模块,您真正想要的是文件结构足够,因此com/foo/bar/a.ts本身就是范围,当您export class A来自该文件时,您不会发生冲突对于任何事情,即使A由另一个文件导出,该另一个文件也是不同的范围。使用者文件将按文件路径import { A } from "./com/bar/a"(或任何相对路径)导入,如果需要避免与其他A导入的名称冲突,可以使用{{1}来处理该导入文件}。

也就是说,我理解重构代码生成以导出没有名称空间的惯用ES6模块可能不可行。我会说你有两个选择:

  1. 返回使用全局命名空间生成代码(无as)并解决构建问题,以便这些文件实际包含在构建中。你在这里没有提到你的方法(只是你有一个运行时export namespace,表明你的设置中没有包含该文件),但如果你能够使用ES6导入/导出我假设您正在使用Webpack或Rollup等模块捆绑器。模块捆绑器旨在捆绑模块,因此包括一些全局代码可以是surprisingly non-trivial。请记住,仅仅因为TypeScript可以编译它,并不意味着您的捆绑包知道如何捆绑它。

  2. 使用ReferenceError通配符将代码生成修改为export命名空间和import,如上例所示。

答案 1 :(得分:1)

你也应该导出你的命名空间:

export namespace com.foo.bar {

  export class A {
    doSomething() {
    }
  }
}

然后:

import * as comFooBar from '../dir/externalLibrary';