繁体   English   中英

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

[英]Import classes from external typescript library which uses 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是否有任何可能性,或者我完全是错误的方式?

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

这个编译错误似乎是事实。 看看你的定义它不是导出一个模块,它只是声明一个全局命名空间。 您可以使用导入别名 (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(); 

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

这意味着您需要在运行的代码中实际包含已编译的externalLibrary.js 这可能就像<script>标签一样简单,或者如果你使用的是捆绑器,你可以将文件作为副作用import "./dir/externalLibrary";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();

但在这一点上,我会说命名空间是没有意义的 只需从模块中导出类:

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模块中使用名称空间被认为是不合时宜的 - 在ES6模块文件存在之前,它们自己定义范围,名称空间(在ES5中只是全局对象/函数闭包)是定义范围的一种方式。 使用ES6模块你真正想要的是文件结构足够,所以com/foo/bar/a.ts本身就是范围,当你从该文件export class A时,你就不会与任何东西发生冲突,即使A由另一个文件导出,另一个文件是不同的范围。 消费者文件将通过文件路径import { A } from "./com/bar/a" (或任何相对路径) import { A } from "./com/bar/a" ,如果需要避免名称与其他A导入冲突,则可以由该导入文件处理使用as

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

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

  2. 修改代码生成以export名称空间,并使用* as通配符import所有文件,如上一个示例所示。

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

 export namespace com.foo.bar { export class A { doSomething() { } } } 

接着:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM