欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

typescript imports 导入 有大用

relative 和 non-relative import

Relative Import以/./../为开头. 如

import Entry from "./components/Entry";
import { DefaultHeaders } from "../constants/http";
import "/mod";

所有其他的都是为non-relative import, 如:

import * as $ from "jquery";
import { Component } from "@angular/core";

relative import引入相对于当前文件路径的文件, 且无法引入ambient module declarations. 使用relative import来引入你自己实现的文件.

non-relative import引入相对于baseUrl路径, 或者存在于路径映射(Path Mapping)中的文件, 可以引入ambient module declarations. 使用non-relative import来引入外部依赖.

模块解析策略

有两种策略: Node或Classic. 通过--moduleResolution来指定策略. 对于--module AMD | System | ES2015来说, 默认是Classic.

Classic (经典策略)

这曾是TypeScript的默认路径解析策略. 现在它主要是为了维护向后兼容性了.

对于relative import, 在文件/root/src/folder/A.ts中`import { b } from "./moduleB", 会进行如下查找:

  1. /root/src/folder/moduleB.ts

  2. /root/src/folder/moduleB.d.ts

对于non-relative import, 查找过程会从当前文件沿着文档树向上查找. 在文件/root/src/folder/A.tsimport { b } from "moduleB", 会进行如下查找:

  1. /root/src/folder/moduleB.ts

  2. /root/src/folder/moduleB.d.ts

  3. /root/src/moduleB.ts

  4. /root/src/moduleB.d.ts

  5. /root/moduleB.ts

  6. /root/moduleB.d.ts

  7. /moduleB.ts

  8. /moduleB.d.ts

Node策略

这个策略模仿Node.js的模块解析策略. Node.js的完整解析算法见这里.

Node.js如何解析模块

Node.js中调用require方法来引入模块.

require的路径为相对路径

文件/root/src/moduleA.js中通过import var x = require("./moduleB");引入模块, 查找过程如下:

  1. 文件/root/src/moduleB.js

  2. 文件夹/root/src/moduleB, 且其中有package.json文件指定了"main"模块. 如/root/src/moduleB/package.json中有{ "main": "lib/mainModule.js" }, 那么Node.js会引入/root/src/moduleB/lib/mainModule.js

  3. 文件夹/root/src/moduleB, 且其中有index.js. 这是默认的"main"模块.

更多见文件模块文件夹模块

require的路径为绝对路径

Node会从node_modules文件夹中寻找模块. node_modules可能是在源文件当前目录中, 也可能是在文件树的上层. Node会沿着文件树逐层向上寻找node_modules中的模块.

文件/root/src/moduleA.js通过import var x = require("moduleB");引入模块, 查找过程如下:

  1. /root/src/node_modules/moduleB.js

  2. /root/src/node_modules/moduleB/package.json (if it specifies a "main" property)

  3. /root/src/node_modules/moduleB/index.js

  4. /root/node_modules/moduleB.js

  5. /root/node_modules/moduleB/package.json (if it specifies a"main"` property)

  6. /root/node_modules/moduleB/index.js

  7. /node_modules/moduleB.js

  8. /node_modules/moduleB/package.json (if it specifies a "main" property)

  9. /node_modules/moduleB/index.js

更多见node_modules引入模块

TypeScript如何解析模块

TypeScript模仿了Node的解析策略, 不过针对的是.ts.tsx.d.ts文件, 而且package.json中用"types"对应于Node中的"main".

相对路径

文件/root/src/moduleA.ts中引入import { b } from "./moduleB", 查找过程如下:

  1. /root/src/moduleB.ts

  2. /root/src/moduleB.tsx

  3. /root/src/moduleB.d.ts

  4. /root/src/moduleB/package.json (if it specifies a "types" property)

  5. /root/src/moduleB/index.ts

  6. /root/src/moduleB/index.tsx

  7. /root/src/moduleB/index.d.ts

绝对路径

文件/root/src/moduleA.ts中引入import { b } from "moduleB", 查找过程如下:

  1. /root/src/node_modules/moduleB.ts

  2. /root/src/node_modules/moduleB.tsx

  3. /root/src/node_modules/moduleB.d.ts

  4. /root/src/node_modules/moduleB/package.json (if it specifies a "types" property)

  5. /root/src/node_modules/moduleB/index.ts

  6. /root/src/node_modules/moduleB/index.tsx

  7. /root/src/node_modules/moduleB/index.d.ts

  8. /root/node_modules/moduleB.ts

  9. /root/node_modules/moduleB.tsx

  10. /root/node_modules/moduleB.d.ts

  11. /root/node_modules/moduleB/package.json (if it specifies a "types" property)

  12. /root/node_modules/moduleB/index.ts

  13. /root/node_modules/moduleB/index.tsx

  14. /root/node_modules/moduleB/index.d.ts

  15. /node_modules/moduleB.ts

  16. /node_modules/moduleB.tsx

  17. /node_modules/moduleB.d.ts

  18. /node_modules/moduleB/package.json (if it specifies a "types" property)

  19. /node_modules/moduleB/index.ts

  20. /node_modules/moduleB/index.tsx

  21. /node_modules/moduleB/index.d.ts

模块解析参数

通常, 项目中都会通过一系列的构建程序将源目录中的文件, 打包/压缩到目标目录中. 这个过程可能导致文件名或目录结构发生变化. 因此TypeScript编译器有如下几个参数应对这种变化.

Base URL

AMD模块加载器, 如requireJS, 常使用baseUrl. 源文件可以在多个目录中, 但目标文件会被放到同一个目录中.

所有non-relative import都相对于baseUrl.

baseUrl的值可以是:

  • baseUrl的命令行参数 (如果参数为相对路径, 则该参数相对于当前路径)

  • tsconfig.json中的baseUrl (如果参数为相对路径, 则该参数相对于tsconfig.json的目录)

更多见RequireJSSystemJS.

Path Mapping (路径映射)

你还可以使用tsconfig.json中的"paths"属性, 定义Path Mapping. 如:

{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is.
    "paths": {
      "jquery": ["node_modules/jquery/dist/jquery"] // This mapping is relative to "baseUrl"
    }
  }
}

注意Path Mapping是相对于baseUrl的.

"paths"可以用来实现路径回退(Fallback)查找. 举个例子.

projectRoot
├── folder1
│   ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│   └── file2.ts
├── generated
│   ├── folder1
│   └── folder2
│       └── file3.ts
└── tsconfig.json

tsconfig.json内容如下

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "*": [
        "*",
        "generated/*"
      ]
    }
  }
}

这个配置让编译器对满足模式"*"(即所有文件)的模块引用, 进行如下查找:

  1. "*": meaning the same name unchanged, so map <moduleName> => <baseUrl>/<moduleName>

  2. "generated/*" meaning the module name with an appended prefix “generated”, so map <moduleName> => <baseUrl>/generated/<moduleName>

所以, 对于file1.ts中的两个引用:

  • import ‘folder1/file2’

  1. "*"捕获文件名folder1/file2

  2. 尝试第一种替换, 即: "*" => folder1/file2

  3. 得到的是绝对路径, 将其与baseUrl结合, 得到projectRoot/folder1/file2.ts

  4. 文件找到, 结束.

  • import ‘folder2/file3’

    1. "*"捕获文件名folder2/file3

    2. 尝试第一种替换, 即: "*" => folder2/file3

    3. 得到的是绝对路径, 将其与baseUrl结合, 得到projectRoot/folder2/file3.ts

    4. 文件不存在, 尝试第二种替换"generated/*" => generated/folder2/file3

    5. 得到的是绝对路径, 将其与baseUrl结合, 得到projectRoot/generated/folder2/file3.ts

    6. 文件找到, 结束.

    import后面的花括号

    export分为两种, Named Export(有名导出)和Default Export(默认导出). 花括号是用于Named Export的.

    Named Export

    // module.ts
    export function A() { ... };
    export function B() { ... };
    
    // app.ts
    import { A, B as BB } from 'module.ts';
    

    其中, 函数AB是Named Export模式, 导入时需要加花括号, 而且可以使用as改名.

    Default Export

    // module.ts
    export default function C () { ... };
    
    // app.ts
    import myFunc from 'module';
    // or
    import { default as myFunc } from 'module';
    

    函数Cdefault方式导出, 引入的时候不用加花括号, myFunc即为C.

    其实default导出也是一种Named Export, 只不过它导出的变量被加了默认的名字default, 所以app.ts中的两句话效果一样.

    export =import = require()

    CommonJS 和 AMD 都有exports对象的概念, exports对象包含了所有导出的内容. 它们也都支持将exports对象替换为自己指定的一个对象/函数/变量等.

    相应地, Typescript使用export =, 导出的内容可以是classinterfacenamespacefunction或者enum.

    使用export =时必须相应地使用import module = require("module").

    // module.ts
    class A { ... }
    export = A;
    // app.ts
    import A = requrie("module");
    

    import * as X from "XXX"是什么意思

    // pet.ts
    export class Dog { ... }
    export class Cat { ... }
    
    // app.ts
    import * as Pet from "./pet.ts";
    let x = new Pet.Dog();
    

    可以看到, import * as X from "XXX"的作用就是从"XXX"文件中, 引入所有导出的内容作为X的属性. 星号 

    import X = require("XXX")作用相同.

    ambient module declarations

    var x = require VS import x from

    在Node.js (CommonJS)中使用 var x = require("XXX"), 其中var也可以用const等修饰词替换. 与之配套的是module.exportsexports.

    exports.A = function() {...}
    module.exports.A = function() {...}
    module.exports = { ... }
    

    ES6采用import x from的形式, 与之配套的是export:

    export function A() { ... }
    export = { ... }
    

    var x = requireimport x = require的区别?

    我现在项目中常看到import x = require.

    参考

    1. ECMAScript 6 modules: the final syntax

    2. Module Resolution

    来自 https://www.jianshu.com/p/e45eb88ea959



    TypeScript 中的多种 import 解义

    19

    JavaScript 中有多种 export 的方式,而 TypeScript 中针对这种情况做了多种 import 语法,最常见的就是 import * as path from 'path' 这种。这篇文章主要来讲解 TypeScript 中不同的 import 具有什么意义。

    原文首发于我的个人网站:听说 - https://tasaid.com,推荐在我的网站阅读更多技术文章。

    前端开发 QQ 群:377786580

    从 export 说起

    有很多朋友都问过我关于 TypeScript 中不同 import 的含义,最典型的就是下面的 import 语法:

    import * as path from 'path'

    不少人疑问这句代码究竟是什么意思,这里我们要先从 js 的 export 开始说。

    首先,JavaScript 的模块化方案,在历史的演进中,有多种导出模块的方式:exportsmodule.exportsexportexport default

    在 nodejs 中内置的模块遵循的都是 CommonJS 规范,语法为 module.exports 和 exports

    // 模块中的 exports 变量指向 module.exports
    // 这篇文章不会深入讲解 module.exports 和 exports 的关系
    
    module.exports = function () { }
    
    exports.site = 'https://tasaid.com'
    
    module.exports.name = 'linkFly'

    例如 nodejs 内置的 events 模块的源码:

    clipboard.png

    在 ECMAScript 6 中又新增了语法 export 和 export default:

    export default function () { }
    
    export const site = 'https://tasaid.com'
    
    export const name = 'linkFly'

    到这里画风还比较正常,而大名鼎鼎的 JavaScript 转码编译器 babel 针对 ECMAScript 6 新增的 export default 语法,搞了个 babel-plugin-transform-es2015-modules-commonjs 的转换插件,用于将 ECMAScript 6 转码为 CommonJs 规范的语法:

    源码:

    export default 42;

    编译后:

    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    
    exports.default = 42;

    到这里,我们看到有三种 export 默认值的语法:

    // commonjs
    module.exports = function () {}
    
    // babel 转码
    exports.default = function () {}
    
    // es6
    export default function () {}

    TypeScript 中的 import

    在 TypeScript 中,也有多种 import 的方式。

    // commonjs 模块
    import * as xx from 'xx'
    
    // es6 模块
    import xx from 'xx'
    
    // commonjs 模块,类型声明为 export = xx
    import xx = require('xx')
    
    // 没有类型声明,默认导入 any 类型
    const xx = require('xx')

    在 tsconfig.json 中,allowSyntheticDefaultImports 会影响到 import 语法的类型检查规则,这个下面再说。

    import * as xx from 'xx'

    import * as xx from 'xx' 的语法来一般都是用来导入使用 module.exports 导出的模块。

    import * as path from 'path'

    因为 nodejs 中的模块大部分都是通过 module.exportsexports.xx 语法进行导出的。

    import xx from 'xx'

    默认情况下,import xx from 'xx' 的语法只适用于 ECMAScript 6 的 export default 导出:

    模块 foo:

    export default function () { 
      console.log('https://tasaid.com') 
    }

    ES6 模块的导入:

    import foo from './foo'
    foo()

    而前面我们说了,babel 会将 es6 的模块的 export default 语法编译为 exports.default 语法。

    而 TypeScript 默认是不识别这种语法的,如果一个模块的导出是 exports.default 导出,如果使用 import xx from 'xx' 的语法导入是会报错的。

    clipboard.png

    所以在 tsconfig.json 中,有个 allowSyntheticDefaultImports 选项,就是针对这种语法做兼容。

    如果设定 allowSyntheticDefaultImports 为 true,则检测导入的模块是否是 ES6 模块,如果不是,则查找模块中是否有 exports.default 导出。

    从而达到针对 exports.default 的兼容。

    clipboard.png

    效果参见这个动画:

    图片描述

    allowSyntheticDefaultImports 选项的,一般情况下我采取的方式是将 deafult 重新命名:

    import { default as foo } from 'foo'

    import xx = require('xx')

    import xx = require('xx') 是用来导入 commonjs 模块的库,特殊的地方在于这个库的类型声明是 export = xx 这种方式导出的:

    foo.js 源码:

    module.exports = () => { 
      console.log('https://tasaid.com') 
    }

    foo.d.ts 类型声明文件源码:

    declare function foo(): void;
    export = foo

    bar.ts 引用:

    import foo = require('./foo')
    
    foo()

    clipboard.png

    我在 《[JavaScript 和 TypeScript 交叉口 —— 类型定义文件(*.d.ts)
    ](https://tasaid.com/blog/20171...》中讲述过 TypeScript 类型声明文件对导入导出的影响。

    const xx = require('xx')

    当一个模块没有类型声明文件的时候,可以使用 commonjs 原始的 require() 方式来导入模块,这样会默认该模块为 any。

    总结

    最后我们整体总结下,在 TypeScript 中,有多种 import 的方式,分别对应了 JavaScript 中不同的 export。

    // commonjs 模块
    import * as xx from 'xx'
    
    // 标准 es6 模块
    import xx from 'xx'
    
    // commonjs 模块,类型声明为 export = xx
    import xx = require('xx')
    
    // 没有类型声明,默认导入 any 类型
    const xx = require('xx')

    针对 babel 编译出来的 exports.default 语法,ts 提供了 allowSyntheticDefaultImports 选项可以支持,只不过个人不太推荐。

    个人建议将 default 重命名。

    import { default as foo } from 'foo'

    关于 TypeScript 中类型声明文件(*.d.ts) 对 import 和 export 的影响,可以参考我之前写的 《[JavaScript 和 TypeScript 交叉口 —— 类型定义文件
    ](https://tasaid.com/blog/20171...》。

    原文首发于我的个人网站:听说 - https://tasaid.com,推荐在我的网站阅读更多技术文章。

    前端开发 QQ 群:377786580

    引用和参考

    来自   https://segmentfault.com/a/1190000018249137


    关于TypeScript模块导入的那些事

     更新时间:2018年06月12日 11:21:58   作者:三毛丶    我要评论
    Typescrit的模块机制与es6的模块基本类似,也提供了转换为amd,es6,umd,commonjs,system的转换,下面这篇文章就来给大家详细介绍了关于TypeScript模块导入的那些事,需要的朋友可以参考借鉴,下面来一起看看吧

    前言

    模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export之一导出它们。 相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用import之一。

    模块是自声明的。在TypeScript里,两个模块之间的关系是通过在文件级别上使用import和export建立的。

    下面话不多说了,来一起看看详细的介绍吧

    ES6 模块导入的限制

    我们先来看一个具体的例子:

    在 Node 项目里,使用 CommonJS 规范引入一个模块:

    const koa = require('koa')

    改写为 TypeScript(1.5+ 版本)时,通常有两种方式:

    使用 ES6 模块导入方式:

    // allowSyntheticDefaultImports: false
    import * as koa from 'koa'

    使用 TypeScript 模块导入语法:

    import koa = require('koa')

    两者大部分是等价的,但 ES6 规范对 import * as 创建出的模块对象有一点限制。

    根据该规范,该模块对象不可被调用,也不可被实例化,它只具有属性。

    因此,如果你想调用该对象,或者使用 new 方法,在 allowSyntheticDefaultImports: false 的配置下,应该使用例子中的第二种方式。

    2.7 版本对 CommonJs/AMD/UMD 模块导入的增强

    在之前的版本,TypeScript 对 CommonJs/AMD/UMD 模块的处理方式与 ES6 模块相同,这会导致一些问题:

    • 如前文所提到的,当导入一个 CommonJs/AMD/UMD 模块时,TypeScript 视 import * as koa from 'koa' 与 const koa = require('koa') 等价,但使用 import * as 创建的模块对象实际上不可被调用以及被实例化。

    • 类似的,当导入一个 CommonJs/AMD/UMD 模块时,TypeScript 视 import foo from 'foo' 与 const koa = require('koa').default 等价,但在大部分 CommonJs/AMD/UMD 模块里,它们并没有默认导出。

    在 2.7 的版本里,TypeScript 提供了一个新选项 --esModuleInterop,旨在解决上述问题,
    当使用该选项,且模块为 CommonJs/AMD/UMD 时,它会导入一个可调用或是可实例化的模块,同时它规定该模块必须作为默认导入:

    import koa from 'koa'
    const app = new koa()

    模块导入仅仅是一些声明类型

    在以非相对路径导入一个模块时,你可能会看到 Could not find a declaration file for module 'someModule' 的错误, 此时你可以安装对应模块的声明文件或者写一个包含 declare module 'someModule' 的声明文件。
    实际上,当我们导入一个模块时:

    import koa from 'koa'
    // import koa = require('koa')

    它所做的事情只有两个:

    • 导入模块的所有类型信息;

    • 确定运行时的依赖关系。

    当你加载此模块,但并没有使用,或仅当作类型来使用时,编译后,此模块将会被移除。

    当不使用时;

    import koa from 'koa'

    编译后:

    仅当做类型使用时:

    import koa from 'koa'
    let k: koa

    编译后:

    var k

    做为值使用时,编译后,此模块将会被保留:

    import koa from 'koa'
    const app = new koa()

    编译后(假设使用 commonjs):

    var __importDefault = (this && this.__importDefault) || function (mod) {
     return (mod && mod.__esModule) ? mod : { "default": mod };
    };
    var koa_1 = __importDefault(require("koa"));
    var k = new koa_1.default();

    注: __importDefault 为使用 --esModuleInterop 选项时产生的方法。

    参考

    http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html
    https://stackoverflow.com/questions/35706164/typescript-import-as-vs-import-require?utm_medium=organ...
    https://basarat.gitbooks.io/typescript/content/docs/project/external-modules.html

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

    普通分类: