我有一个现有的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是否有任何可能性,或者我完全是错误的方式?
答案 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模块可能不可行。我会说你有两个选择:
返回使用全局命名空间生成代码(无as
)并解决构建问题,以便这些文件实际包含在构建中。你在这里没有提到你的方法(只是你有一个运行时export namespace
,表明你的设置中没有包含该文件),但如果你能够使用ES6导入/导出我假设您正在使用Webpack或Rollup等模块捆绑器。模块捆绑器旨在捆绑模块,因此包括一些全局代码可以是surprisingly non-trivial。请记住,仅仅因为TypeScript可以编译它,并不意味着您的捆绑包知道如何捆绑它。
使用ReferenceError
通配符将代码生成修改为export
命名空间和import
,如上例所示。
答案 1 :(得分:1)
你也应该导出你的命名空间:
export namespace com.foo.bar {
export class A {
doSomething() {
}
}
}
然后:
import * as comFooBar from '../dir/externalLibrary';