👉 TypeScript学习:TypeScript从入门到精通 👉 蓝桥杯真题解析:蓝桥杯Web国赛真题解析
👉 个人简介:即将大三的学生,热爱前端,热爱生活🍬
👉 你的一键三连是我更新的最大动力❤️!
🏆分享博主自用牛客网🏆:一个非常全面的面试刷题求职网站,真的超级好用🍬
前言
博主一直在牛客网刷题巩固基础知识,快来和我一起冲关升级吧!点击进入牛客网
最近博主一直在创作TypeScript
的内容,所有的TypeScript
文章都在我的TypeScript从入门到精通专栏里,每一篇文章都是精心打磨的优质好文,并且非常的全面和细致,期待你的订阅❤️
本篇文章将深入去讲解TypeScript
中的模块化,这也许会是你看过的最全面最细致的TypeScript
教程,点赞关注收藏不迷路🚀🚀🚀!
文章目录
- 前言
- 1、模块定义
- 2、ES模块语法
- 导出别名
- 二次导出
- TS特定的语法
- 3、CommonJS语法
- 4、环境模块
- 速记环境模块
- 5、TypeScript模块选项
- 模块解析选项
- 模块输出选项
- 6、TypeScript命名空间
- 结语
1、模块定义
在TypeScript
中,就像在EC5
中一样,任何包含顶级import
或export
的文件都被认为是一个模块
相反的,一个没有任何顶级导入或导出声明的文件被视为一个脚本,其内容可在全局范围内使用(因此也可用于模块)
模块在自己的范围内执行,而不是在全局范围内。这意味着在模块中声明的变量、函数、类等在模块外是不可见的,除非它们被明确地用某种导出形式导出。相反,要使用从不同模块导出的变量、函数、类、接口等,必须使用导入的形式将其导入。
JavaScript
规范声明,任何没有export
或顶层 await
的JavaScript
文件都应该被认为是一个脚本而不是一个模块
如果你有一个目前没有任何导入或导出的文件,但你希望它被当作一个模块来处理,可以添加这一行:
export {};
这将改变该文件,使其成为一个什么都不输出的模块。无论你的模块目标是什么,这个语法都有效
TypeScript
中能够使用JavaScript
的模块化语法,并在此基础上提供了一些额外的语法
2、ES模块语法
🚗 一个文件可以通过export default
声明一个主要出口:
// @filename: hello.ts
export default function helloWorld() {
console.log("Hello, world!");
}
一个文件中export default
只能有一个
通过import
导入:
// @filename: a.ts(与 hello.ts同级)
import hello from "./hello";
hello();
import
引入export default
导出的内容时可以自定义导入名称,如上面导出的函数名为helloWorld
,但引入时我们自定义了hello
的名称
default
出口也可以只是数值:
// @filename: hello.ts
export default "123";
// @filename: a.ts(与 hello.ts同级)
import h from "./hello";
console.log(h); // "123"
🚗 除了默认的导出,还可以通过省略default
的export
,导出多个变量和函数的:
// @filename: hello.ts
export var a = 3.14;
export let b = 1.41;
export const c = 1.61;
export class D {}
export function fn(num: number) {
console.log(num);
}
可以只使用一个export
导出:
var a = 3.14;
let b = 1.41;
const c = 1.61;
class D {}
function fn(num: number) {
console.log(num);
}
export { a, b, c, D, fn };
通过import
和{}
实现按需导入:
// @filename: a.ts(与 hello.ts同级)
import { a, b, c, D, fn } from "./hello";
console.log(a, b, c, new D());
fn(1);
可以使用 import {old as new}
这样的格式来重命名一个导入:
// @filename: a.ts(与 hello.ts同级)
// 仅引入a,c,fn 并重命名a和fn
import { a as A, c, fn as FN } from "./hello";
console.log(A, c);
FN(1);
可以把所有导出的对象,用* as name
,把它们放到同一个命名空间name
:
// @filename: a.ts(与 hello.ts同级)
// export导出的所有内容放到了命名空间 F 中
import * as F from "./hello";
console.log(F.a, F.c);
F.fn(1);
🚗 export default
与export
一起使用:
// @filename: hello.ts
export var a = 3.14;
export let b = 1.41;
export const c = 1.61;
export class D {}
export function fn(num: number) {
console.log(num);
}
export default function helloWorld() {
console.log("Hello, world!");
}
// @filename: a.ts(与 hello.ts同级)
import hello, { a, b, c, D, fn } from "./hello";
console.log(a, b, c, new D());
fn(1);
hello();
🚗
通过import "file Path"
导入一个文件,而不把任何变量纳入你的当前模块:
// @filename: a.ts(与 hello.ts同级)
import "./hello";
在这种情况下, import
没有任何作用,但 hello.ts
中的所有代码都将被解析,这可能引发影响其他对象的副作用
导出别名
像导入时使用as
定义别名一样,在导出时也可以使用as
定义导出的别名:
// @filename: hello.ts
const a = 1;
export { a as A };
// @filename: a.ts(与 hello.ts同级)
import { A } from "./hello";
console.log(A); // 1
二次导出
🚗 一个模块可以引入并导出另一个模块的内容,这称为二次导出,一个二次导出并不在本地导入,也不引入本地变量
hello.ts
:
// @filename: hello.ts
export const a = "hello";
export const n = 1;
word.ts
(与hello.ts
同级):
// @filename: word.ts(与hello.ts同级)
export const b = "word";
// 该模块扩展了hello.ts模块,并向外暴露hello.ts模块的a
export { a } from "./hello";
a.ts
(与 hello.ts
同级):
// @filename: a.ts(与 hello.ts同级)
import { a, b } from "./word";
console.log(a, b); // hello word
🚗 另外,一个模块可以包裹一个或多个模块,并使用 export * from "module "
语法组合它们的所有导出:
word.ts
(与hello.ts
同级):
// @filename: word.ts(与hello.ts同级)
export const b = "word";
// 相当于将hello.ts导出的内容全部引入后又全部导出
export * from "./hello";
// 此文件相当于导出了b和a、n(来自hello.ts)
a.ts
(与 hello.ts
同级):
// @filename: a.ts(与 hello.ts同级)
import { a, b, n } from "./word";
console.log(a, b, n); // hello word 1
🚗 export * from "module "
语法也可以使用别名as
:export * as ns
作为一种速记方法来重新导出另一个有名字的模块
word.ts
(与hello.ts
同级):
// @filename: word.ts(与hello.ts同级)
export const b = "word";
// 相当于将hello.ts导出的内容全部引入到命名空间H中,后又将H导出
export * as H from "./hello";
a.ts
(与 hello.ts
同级):
// @filename: a.ts(与 hello.ts同级)
import { H, b } from "./word";
console.log(H.a, b, H.n); // hello word 1
TS特定的语法
类型可以使用与JavaScript
值相同的语法进行导出和导入:
// @filename: hello.ts
export type Cat = {};
export interface Dog {}
// @filename: a.ts(与 hello.ts同级)
import { Cat, Dog } from "./hello";
let a: Cat, b: Dog;
TypeScript
用两个概念扩展了 import
语法,用于声明一个类型的导入:
🚗 import type
这是一个导入语句,导入的变量只能用作类型:
// @filename: hello.ts
export const createCatName = () => "fluffy";
🚗
TypeScript 4.5
还允许以type
为前缀的单个导入,以表明导入的引用是一个类型:
// @filename: hello.ts
export type Cat = {};
export interface Dog {}
export const createCatName = () => "fluffy";
// @filename: a.ts(与 hello.ts同级)
// 表明Cat和Dog为类型
import { createCatName, type Cat, type Dog } from "./hello";
type Animals = Cat | Dog;
const name = createCatName();
🚗 export =
与import = require()
:
export =
语法指定了一个从模块导出的单一对象,这可以是一个类,接口,命名空间,函数,或枚举,当使用export =
导出一个模块时,必须使用TypeScript
特定的import module=require("module")
来导入模块:
// @filename: hello.ts
const a = "hello";
export = a;
// @filename: a.ts(与 hello.ts同级)
import H = require("./hello");
console.log(H); // hello
3、CommonJS语法
若使用CommonJS语法报错,则需要先在项目根目录运行:npm i --save-dev @types/node
安装声明文件
通过在一个全局调用的 module
上设置 exports
属性来导出:
// @filename: hello.ts
function absolute(num: number) {
return num;
}
const a = 3;
let b = 4;
var c = 5;
module.exports = {
a,
b,
newC: c, // 将c以newC的名称导出
d: 12, // 直接导出一个值
fn: absolute, // 将absolute以fn的名称导出
};
通过require
语句导入:
// @filename: a.ts(与 hello.ts同级)
const m = require("./hello");
console.log(m.a, m.b, m.newC, m.fn(1));
使用JavaScript
中的解构功能来简化一下:
// @filename: a.ts(与 hello.ts同级)
const { d, fn } = require("./hello");
console.log(d, fn(1));
4、环境模块
提前声明:declare module
并不仅限于.d.ts
文件,在普通ts
文件中也可使用
.d.ts
和declace
的介绍可见大佬文章:ts的.d.ts和declare究竟是干嘛用的
我们可以使用顶级导出声明declare
在自己的.d.ts
文件中定义模块(这类似于命名空间),需要使用module
关键字和模块的引用名称,这些名称将可用于以后的导入,例如:
// type.d.ts
declare module "A" {
export type a = string;
// ...
}
declare module "B" {
export type a = number;
// ...
}
上面我们在type.d.ts
声明文件中使用declare module
的形式定义了两个环境模块 A
和B
,使用时根据环境模块的名称(A
或B
)使用import
语句可直接将其导入:
import { a } from "A";
let num: a = "1"; // type num =string
导入时同样支持别名等import
语法:
import * as moduleA from "A";
let num: moduleA.a = "1"; // type num =string
外部文件只能引用环境模块declare module
内的类型声明!
速记环境模块
如果您不想在使用新模块之前花时间写出声明,或者你的新模块还没有任何类型声明,你可以使用速记环境模块来快速定义一个空内容模块:
// type.d.ts
declare module "User";
所有来自速记模块的导入都将具有any
类型:
import a, { c } from "User";
a(c);
速记模块就是空内容的环境模块,这其实没多大用处
5、TypeScript模块选项
模块解析选项
模块解析是指从import
或require
语句中获取一个字符串,并确定该字符串所指的文件的过程
TypeScript
包括两种解析策略:
- 经典
-
Node
当编译器选项(tsconfig.json
配置文件)中 module
不是commonjs
时,经典策略是默认的,是为了向后兼容。
Node
策略复制了Node.js
在CommonJS
模式下的工作方式,对.ts
和.d.ts
有额外的检查
在TypeScript
中,有许多TSConfig
标志影响模块策略:
- moduleResolution
- baseUrl
- paths
- rootDirs
关于这些策略如何工作的全部细节,你可以参考《模块解析》
模块输出选项
tsconfig.json
配置文件中有两个选项影响JavaScript
的输出:
-
target
,它决定了TS
代码编译成JS
的版本 -
module
,它决定了哪些代码用于模块之间的相互作用
所有模块之间的通信都是通过模块加载器进行的,编译器选项 module
决定了使用哪一个
在运行时,模块加载器负责在执行一个模块之前定位和执行该模块的所有依赖项
可以在TSConfig 模块参考 中看到所有可用的选项以及它们编译出的JavaScript
代码是什么样子
6、TypeScript命名空间
TypeScript
有自己的模块格式,称为 命名空间(namespaces)
,这比ES
模块标准要早
这种语法对于创建复杂的定义文件有很多有用的功能,并且在DefinitelyTyped
中仍然被积极使用。虽然没有被废弃,但命名空间中的大部分功能都存在于ES Modules
中,官方建议使用它来与JavaScript
的方向保持一致
更多关于命名空间的信息可见: 【TypeScript】深入学习TypeScript命名空间
结语
至此,TypeScript
模块化的内容就全部结束了,关注博主下篇更精彩!
博主的TypeScript从入门到精通专栏正在慢慢的补充之中,赶快关注订阅,与博主一起进步吧!期待你的三连支持。
参考资料:TypeScript官网
如果本篇文章对你有所帮助,还请客官一件四连!❤️
基础不牢,地动山摇! 快来和博主一起来牛客网刷题巩固基础知识吧!