- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我对 module/namespace/export
感到很困惑和 import, require, reference
用法。
来自 Java 背景,有人可以简单地解释一下什么时候使用什么以及什么是正确的设计?我在写示例项目的时候感觉自己搞砸了
到目前为止,这是我的理解
1. module
用于外部包
2. namespace
用于内部包
最佳答案
我不明白我们是如何分类的?
命名空间用于组织/封装您的代码。外部模块用于组织/封装您的代码并在运行时定位您的代码。实际上,您在运行时有两种选择:1) 将所有转译的代码合并到一个文件中,或者 2) 使用外部模块并拥有多个文件,并且需要某种其他机制来获取这些文件。
何时导出类、命名空间或包?
要使类型或值在它所在的文件之外可见,如果它在命名空间内,则必须将其导出。是在顶层还是在命名空间中导出它将决定它现在是否在外部模块中。
如果我们导出包/命名空间,其中的所有类都被导出或需要显式导出
命名空间中的类总是需要显式导出,以便类在编译时在定义它的文件之外可见。
如何导入/需要它们中的每一个?
这取决于您是否使用外部模块。始终需要导入外部模块才能“使用”它。导入不在外部模块中的命名空间实际上只是为命名空间提供了一个别名——您仍然必须在类型/任何内容前加上别名(这就是为什么您通常不想将命名空间与外部模块一起使用;这样做意味着您在引用外部模块提供的任何内容时始终必须使用前缀。)不在外部模块中的命名空间可以跨越文件,因此如果您在同一个命名空间中,您可以引用由外部模块导出的任何内容命名空间而无需任何类型的导入。
要真正理解上述内容,您需要一些背景知识。理解引用/命名空间/外部模块的关键是这些构造在编译时的作用以及它们在运行时的作用。
在编译时使用引用指令来定位类型信息。您的来源中有一个特定的符号。 TypeScript 编译器如何定位该符号的定义?引用指令在很大程度上被 tsconfig.json 机制包含——使用 tsconfig.json,你告诉编译器你所有的源在哪里。
命名空间可以包含类型定义和/或实现。如果一个命名空间只包含类型信息,那么它根本没有运行时表现——你可以通过查看 JS 输出并找到一个空的 JS 文件来检查这一点。如果命名空间有实现代码,那么代码被包装在一个闭包中,该闭包被分配给一个与命名空间同名的全局变量。对于嵌套命名空间,根命名空间将有一个全局变量。再次检查 JS 输出。命名空间历来是 JS 客户端库试图避免命名冲突问题的方式。这个想法是将您的整个库包装到一个闭包中,然后公开尽可能小的全局足迹——只有一个引用闭包的全局变量。好吧,问题仍然是你在全局空间中声明了一个名字。如果你想要一个库的两个版本怎么办? TypeScript 命名空间仍然存在如何定位命名空间源的问题。也就是说,引用 A.B 的源代码仍然存在告诉编译器如何定位 A.B 的问题——通过使用引用指令或使用 tsconfig.json。或者通过将命名空间放入外部模块,然后导入外部模块。
外部模块起源于服务器端 JS。外部模块和文件系统上的文件是一一对应的。您可以使用文件系统目录结构将外部模块组织成嵌套结构。导入外部模块通常会引入对该外部模块的运行时依赖(异常(exception)情况是当您导入外部模块但随后不在值位置使用其任何导出时——也就是说,您只导入外部模块以获取其类型信息)。外部模块隐含在闭包中,这是关键:模块的用户可以将闭包分配给他们想要的任何局部变量。 TypeScript/ES6 添加了额外的语法,将外部模块的导出映射到本地名称,但这只是一个细节。在服务器端,定位外部模块相对简单:只需在本地文件系统上定位代表外部模块的文件即可。如果您想在客户端使用外部模块,在浏览器中,它会变得更加复杂,因为没有与具有可加载模块的文件系统等效的文件系统。所以现在在客户端,你需要一种方法将所有这些文件捆绑成一个可以在浏览器中远程使用的表单——这就是像 Webpack 这样的模块捆绑器(尽管 Webpack 做的比捆绑模块要多得多)和Browserify 开始发挥作用。模块捆绑器允许在浏览器中对外部模块进行运行时解析。
真实世界场景:AngularJS。假装外部模块不存在,使用单一命名空间来限制全局空间的污染(在下面的示例中,单个变量 MyApp 是全局空间中的全部),仅导出接口(interface),并使用 AngularJS 依赖注入(inject)进行实现可供使用。把所有的类放在一个目录根目录下,在根目录下添加一个tsconfig.json,在同一个目录根目录下安装angularjstypings,这样tsconfig.json也能捡起来,把所有的输出合并成一个JS文件。如果代码重用不是什么大问题,这对于大多数项目来说都可以正常工作。
我的服务.ts:
namespace MyApp {
// without an export the interface is not visible outside of MyService.ts
export interface MyService {
....
}
// class is not exported; AngularJS DI will wire up the implementation
class MyServiceImpl implements MyService {
}
angular.module("MyApp").service("myService", MyServiceImpl);
}
namespace MyApp {
class MyController {
// No import of MyService is needed as we are spanning
// one namespace with multiple files.
// MyService is only used at compile time for type checking.
// AngularJS DI is done on the name of the variable.
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
}
namespace Foo {
// without an export IFoo is not visible. No JS is generated here
// as we are only defining a type.
export interface IFoo {
x: string;
}
}
interface ITopLevel {
z: string;
}
(function(){
// export required above to make IFoo visible as we are not in the Foo namespace
class Foo1 implements Foo.IFoo {
x: string = "abc";
}
// do something with Foo1 like register it with a DI system
})();
// alias import; no external module created
import IFoo = Foo.IFoo;
(function() {
// Namespace Foo is always visible as it was defined at
// top level (outside of any other namespace).
class Bar1 implements Foo.IFoo {
x: string;
}
// equivalent to above
class Bar2 implements IFoo {
x: string;
}
// IToplevel is visible here for the same reason namespace Foo is visible
class MyToplevel implements ITopLevel {
z: string;
}
})();
interface MyService {
....
}
(function() {
class MyServiceImpl implements MyService {
}
angular.module("MyApp").service("myService", MyServiceImpl);
})();
(function() {
class MyController {
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
})();
关于typescript - 模块 vs 命名空间 - 导入 vs 需要 typescript ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38582352/
我已经写了并且 npm 发布了这个:https://github.com/justin-calleja/pkg-dependents 现在我正在用 Typescript 编写这个包:https://g
我有一个函数,我想在 TypeScript 中模拟它以进行测试。在我的测试中,我只关心 json和 status .但是,当使用 Jest 的 jest.spyOn 时我的模拟函数的类型设置为返回 h
我正在使用一个库 (Axios),它的包中包含 Typescript 声明。 我想声明一个将 AxiosResponse(在库的 .d.ts 文件中声明)作为参数的函数。我有以下内容: functio
我是 Typescript 的新手。我想使用 将一个 Typescript 文件加载到另一个 Typescript 文件中标签。 我做了一些事情,但它不起作用!请帮助我。 first.ts: imp
为什么我会收到下面屏幕截图中显示的错误? Atom 说我的 tsconfig.json“项目文件包含无效选项”用于 allowJs、buildOnSave 和 compileOnSave。 但是应该允
所以我正在创建一个 TypeScript 库,我可以轻松地将所有生成的 JS 文件编译成一个文件。有没有办法将所有 .ts 和 .d.ts 编译成一个 .ts 文件? 除了支持 JS 的版本(较少的智
Microsoft Research 提供了一种名为Safer TypeScript 的新 TypeScript 编译器变体: http://research.microsoft.com/en-us/
我需要这个来在单个文件中分发 TypeScript 中的库。有没有办法将多个 typescript 文件合并到(一个js文件+一个 typescript 定义)文件中? 最佳答案 要创建一个库,您可以
用例:我想知道一个函数在 typescript 中执行需要多少时间。我想为此目的使用装饰器。我希望装饰器应该返回时间以便(我可以进一步使用它),而不仅仅是打印它。 例如: export functio
我想检查一个类型是否可以为 null,以及它是否具有值的条件类型。 我尝试实现 type IsNullable = T extends null ? true : false; 但是好像不行 type
我的问题是基于这个 question and answer 假设我们有下一个代码: const myFn = (p: { a: (n: number) => T, b: (o: T) => v
我知道双重否定前缀,我知道 TypeScript 的单后缀(非空断言)。 但是这个双后缀感叹号是什么? /.*验证码为(\d{6}).*/.exec(email.body!!)!![1] 取自here
我正在使用以下文件结构在 Webstorm 中开发一个项目 | src | ... | many files | types | SomeInterface |
在 TypeScript 类中,可以为属性声明类型,例如: class className { property: string; }; 如何在对象字面量中声明属性的类型? 我试过下面的代码,但它
我正在寻找一种在不丢失推断类型信息的情况下将 TypeScript 中的文字值限制为特定类型的好方法。 让我们考虑一个类型Named,它保证有一个名字。 type Named = { name:
在 TypeScript 中,我想创建一个联合类型来表示属于一个或多个不同类型的值,类似于 oneOf在 OpenAPI或 JSON Schema .根据a previous answer on a
type Func = (foo:string) => void // function expression const myFunctionExpression:Func = function(f
假设我有一个联合类型,我正在使用类似 reducer 的 API 调用模式,看起来像这样: type Action = { request: { action: "create
我在 typescript 中有以下去抖功能: export function debounce( callback: (...args: any[]) => void, wait: numb
在 Vue3 的 defineComponent 函数中,第一个泛型参数是 Props,所以我在这里使用 Typescript 接口(interface)提供我的 props 类型。喜欢: expor
我是一名优秀的程序员,十分优秀!