TypeScript 和 JavaScript 一样。如果一个文件不带有顶级的 import 或 export 声明,就认为它是一个脚本,声明的变量全局作用域可见;反之,就认为它是一个模块,有独立作用域。
命名空间:
命名空间使用 namespace 关键字定义,通过 export 关键字将内容暴露给外部使用,具有独立的作用域。主要是为了避免全局命名冲突。
在 ES Module 之前,TypeScript 支持命名空间。目前,命名空间虽然没有被废弃掉,但官方更推荐使用 ES Module,所以最好不要再使用了。
// index.ts 脚本文件 // 直接在一个脚本文件中定义相同的变量,出现了命名冲突 function format() {} function format() {}
// index.ts 脚本文件 // 使用命名空间,有了独立的作用域,不会再命名冲突了 namespace price { function format() {} } namespace date { function format() {} }
定义命名空间:
命名空间可以导出变量、函数、类、接口、类型等。通过 export
关键字将内容暴露给外部使用。
namespace MyNamespace {
// 导出变量
export const myVar = 123;
// 导出函数
export function myFunc() {
console.log("Hello from MyNamespace");
}
// 导出类
export class MyClass {
constructor(public name: string) {}
}
// 导出类型
export type MyType = string | number;
// 导出接口
export interface MyInterface {
id: number;
name: string;
}
}
在同一文件中使用命名空间:
// 使用命名空间中的内容
const myVar = MyNamespace.myVar;
const obj: MyNamespace.MyInterface = { id: 1, name: "Test" };
跨文件使用命名空间:
需要通过 <reference>
标签引入命名空间。
// 引入命名空间
///<reference path='./MyNamespace.ts'>
// 使用命名空间中的内容
const myVar = MyNamespace.myVar;
const obj: MyNamespace.MyInterface = { id: 1, name: "Test" };
模块
TypeScript 支持很多模块化的方案,但最主要的是 ES Module。
定义模块并将其导出、导入模块并对其使用:
// myModule.ts
// 导出变量
export const myVar = 123;
// 导出函数
export function myFunc() {
console.log("Hello from myModule");
}
// 导出类
export class MyClass {
constructor(public name: string) {}
}
// 导出接口
export interface MyInterface {
id: number;
name: string;
}
// 导出类型
export type MyType = string | number;
// main.ts
import { myVar, MyInterface } from './myModule';
const myVarValue = myVar;
const obj: MyInterface = { id: 1, name: "Test" };
类型的导出和导入:
除了支持 JavaScript 中 ES Module 的功能外,TypeScript 还支持类型的导出和导入。
其中,导入类型时最好添加 type 前缀来表明导入的是一个类型,因为 TypeScript 的类型只是在编写代码的过程中提供类型检测,在编译后都会被移除的,这可以让非 TypeScript 的编译器(例如:Babel)知道什么样的导入可以被安全移除。
// type.ts
// 导出类型
export interface IPerson {
name: string,
age: number,
}
export type IDType = number | string
// index.ts
// 导入类型
import {type IPerson. type IDType} from "./type"
// 也可以使用这种方式:
import type {IPerson, IDType } from "./type"
// 使用类型
let p: IPerson = {
name: 'Lee',
age: 18,
}
let id: IDType = 1
命名空间和模块的区别:
- 定义方式:命名空间使用 namespace 关键字定义,通过 export 关键字将内容暴露给外部使用,主要是为了避免全局命名冲突;模块 import 和 export 关键字来定义导入导出。
- 作用域:命名空间的内容在全局作用域中是相对可见的,通过
命名空间名称.
的方式来访问;而模块的内容是局部可见的,仅限于模块内部或显式导出的部分。 - 加载方式:命名空间不支持按需加载,一旦一个命名空间被引用,它的所有内容都会被加载;模块支持按需加载。
- 现代实践:目前,命名空间虽然没有被废弃掉,但官方更推荐使用 ES Module。