翼度科技»论坛 编程开发 JavaScript 查看内容

TS 导入导出那些事

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
前言

最近用 TypeScript 写 npm 包,各种模块、命名空间、全局定义等等扰得我睡不着觉。
我便苦心研究,总结了几个比较冷门的,国内貌似基本上找不到资料的导入导出用法,顺便在其中又插入一些不那么冷门的用法,于是本篇文章来了。
因为一开始也没想做成大全,可能之后还会继续更新吧。
目录

导入模块

导入模块中的东西相信大家都不陌生。唯一需要注意的便是默认导出与“星号”导出的区别。
  1. import * as Mod from './mod';
  2. // 类似于
  3. const Mod = require('./mod');
复制代码
  1. import ModDef from './mod';
  2. // 类似于
  3. const { default: ModDef } = require('./mod');
复制代码
  1. import ModDef, { a, b } from './mod';
  2. // 类似于
  3. const {
  4.         default: ModDef,
  5.         a, b
  6. } = require('./mod');
复制代码
在模块中导出

在模块中导出东西相信大家也不陌生,不过这里还是详细讲解一下。
在模块中导出东西有很多种方法。导出总共可分为 4 类:
命名导出

命名导出有两种方法,一种是声明着导出
  1. export namespace A { }
  2. export function b() { }
  3. export class C { }
  4. export const d = 123;
  5. export let { e } = { e: 'hh' };
复制代码
一种是声明后导出
  1. namespace A { }
  2. function b() { }
  3. class C { }
  4. const d = 123;
  5. let { e } = { e: 'hh' };
  6. export { A, b, C, d, e };
复制代码
声明后导出比声明着导出更灵活,能合并,也能重命名
  1. namespace A { }
  2. export { A };
  3. function b() { }
  4. export { b as c };
  5. class C { }
  6. export { C as B };
  7. const d = 123;
  8. let { e } = { e: 'hh' };
  9. export { d, e };
复制代码
命名导出编译成 Common JS 后类似这样
  1. exports.xxx = xxx;
复制代码
需要注意的是其他人无法修改任何你导出的东西。即使是使用 let 声明也一样
  1. /* mod.ts */
  2. export let a = 123;
  3. /* others.ts */
  4. import { a } from './mod';
  5. a = 321; // 报错:ts(2632)
复制代码
不过对于上面的代码,你可以随便修改所导出的 a 。因为其他人每次读取 a 时都会重新从你的导出对象上访问一次 a 属性,不用担心其他人无法接收到你的修改。具体可以查看编译后的 JS 文件
  1. /* others.ts */
  2. import { a } from './mod';
  3. const b = a + 123;
  4. console.log(a);
  5. /* others.js */
  6. var mod_1 = require("./mod");
  7. var b = mod_1.a + 123;
  8. console.log(mod_1.a);
复制代码
默认导出

默认导出可以理解为一种特殊的命名导出。
默认导出的名字是 default 。但是你不能搞个名字叫 default 的变量然后导出,你必须得用 export default 或者在导出时重命名
  1. export let default = 123; // 报错:ts(1389)
  2. export default 123; // 正确
  3. export let a = 123;
  4. export { a as default }; // 正确
复制代码
星号导入搭配默认导出,可以达到默认导出即为星号导出的效果
  1. /* mod.ts */
  2. import * as Self from './mod';
  3. export default Self;
  4. /* others.ts */
  5. import * as Mod from './mod';
  6. import ModDef from './mod';
  7. console.log(Mod === ModDef); // true
复制代码
星号导出

星号导出可以导出其他模块里的东西。一般有两种用法。
第一种是全导出到自己里头,就像
  1. export * from './xxx'
复制代码
具体效果是 xxx 中导出的东西,也可以通过你访问到。
  1. /* xxx.ts */
  2. export let a = { hh: 'hh' };
  3. /* mod.ts */
  4. export * from './xxx.ts';
  5. /* others.ts */
  6. import { a } from './xxx';
  7. import { a } from './mod';
  8. console.log(a === a); // true
复制代码
第二种是挂到自己模块下面,就像
  1. export * as xxx from './xxx';
  2. // 等价于
  3. import * as xxx from './xxx';
  4. export { xxx };
复制代码
导出分配

导出分配就是把 Common JS 的导出搬到了 TS 中。写法也差不多
  1. export = 'hh';
  2. // 相当于
  3. module.export = 'hh';
复制代码
导出分配也可以指定默认导出,只需要有 default 属性就可以
  1. /* mod.ts */
  2. export = { default: 123 };
  3. /* others.ts */
  4. import mod from './mod';
  5. console.log(mod); // 123
复制代码
需要注意的是采用了导出分配后便不能再使用其他导出方法。
导入命名空间

虽然现在命名空间相较于模块并不是特别常用,但它还是有比较完整的导入导出功能的。
导入命名空间中的东西很简单,像这样
  1. import xxx = Space.xxx;
复制代码
不论你是在模块中导入全局命名空间,还是在命名空间中导入其他命名空间,都是适用的。
  1. import Err = globalThis.Error;
  2. throw Err('hh');
  3. namespace A {
  4.         import Process = NodeJS.Process;
  5.         let proce: Process;
  6. }
复制代码
较为可惜的是命名空间貌似没有星号导入,也不支持解构导入。
在命名空间中导出

在一般 TS 中,命名空间只有两种方法导出。
第一种方法是声明着导出,类似于模块
  1. namespace A {
  2.         export const a = 123;
  3. }
复制代码
第二种方法是导入着导出,可以用来导出其他命名空间的东西
  1. namespace A {
  2.         export import Err = globalThis.Error;
  3. }
复制代码
而对于不一般的 TS ——也就是类型声明中,命名空间还可以采用像模块一样的导出对象
  1. declare namespace A {
  2.         const a = 123;
  3.         const b = 'hh';
  4.         export { a, b };
  5. }
复制代码
使用全局定义

全局定义一般有三种:

  • 内置在 TS 中的全局定义。比如 setTimeout 、 Error 等。
    对于这种全局定义,直接拿来用就可以了。
  • 位于环境模块中的全局定义。比如 NodeJS.Process 等。
    包括位于 node_modules/@types 文件夹中的自动引入的环境模块,都可以通过三斜杠注释来引入。
    你可以通过 path 直接指定文件路径
    1. /// <reference types="@babel/core" /><reference path="./types.d.ts" />
    复制代码
  • 位于模块中的全局定义。
    这种全局定义只需要引入一下模块,表示你已经运行此模块,即可
    1. import '@babel/core';
    复制代码
    或者你也可以通过三斜杠注释,通过 types 指定模块
    1. /// <reference types="@babel/core" /><reference path="./types.d.ts" />
    复制代码
    需要注意的是,不论你采用 import 还是三斜杠注释,甚至只是在类型中使用了一个 typeof import('xxx') ,只要你在一个 TS 文件中引入了这个模块所定义的全局类型,那这个类型就会永远存在下去,污染你的 globalThis 。
    唯一在不污染全局域的情况下运行模块的方法是使用 import() 函数动态引入,但这样子你也拿不到你需要的类型。
进行全局定义

进行全局定义一般有三种方法。
第一种是直接写环境模块。不带任何 import 和 export 一般就会让编译器把这当成一个环境模块。所以,如果你需要防止一个 TS 文件变成环境模块导致类型泄露的话,你可以加一个安全无公害的 export { }; 。
第二种是在模块中定义,只需要把类型定义写到 declare global 里头就行
  1. declare global {
  2.         const a = 123;
  3.         let b: {};
  4. }
  5. a; // 123
  6. b; // {}
复制代码
第三种是通过合并 globalThis 命名空间来定义,好处是可以使用命名空间的“导入着导出”方法,将模块或者其他命名空间的局部变量变成全局变量
  1. import _Mod from './mod';
  2. declare global {
  3.         namespace globalThis {
  4.                 const a = 123;
  5.                 export import Mod = _Mod;
  6.         }
  7. }
  8. a; // 123
  9. Mod; // typeof import('./mod')
复制代码
来源:https://www.cnblogs.com/QiFande/p/ts-about-export.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具