project-root/
├── src/
│ ├── components/
│ │ ├── Button.tsx
│ │ └── Input.tsx
│ ├── pages/
│ │ ├── HomePage.tsx
│ │ └── AboutPage.tsx
│ ├── utils/
│ │ ├── api.ts
│ │ └── format.ts
│ ├── index.ts
│ └── global.d.ts
├── tests/
│ ├── unit/
│ │ ├── Button.test.ts
│ │ └── api.test.ts
│ └── integration/
│ └── HomePage.test.ts
├── dist/
├── node_modules/
├── package.json
├── tsconfig.json
├── .gitignore
└── README.md
(1)src 目录:存放项目的源代码,是开发的核心区域。
(2)tests 目录:包含单元测试和集成测试的代码,用于保证代码的质量和功能正确性。
(3)dist 目录:编译后的 JavaScript 文件会输出到这里,用于部署到生产环境。
(4)node_modules 目录:项目依赖的第三方库会安装在此目录下。
(5)package.json:记录项目的元数据、依赖信息以及脚本命令等。
(6)tsconfig.json:TypeScript 编译器的配置文件,决定了编译的行为和规则。
(7)gitignore:指定哪些文件和目录不需要被纳入版本控制。
(8)README.md:项目的说明文档,介绍项目的基本信息、使用方法等。
编译上下文指的是 TypeScript 编译器在编译过程中所考虑的一组文件。编译器会根据 tsconfig.json 中的配置来确定哪些文件属于编译上下文。
include 指定了需要包含在编译上下文中的文件或目录路径。例如:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
},
"include": ["src/**/*.ts"]
}
上述配置表示将 src 目录下的所有 .ts 文件纳入编译上下文。
exclude 则指定了需要从编译上下文中排除的文件或目录路径。例如:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
},
"include": ["src/**/*.ts"],
"exclude": ["src/tests/**/*.ts"]
}
这样就会把 src/tests 目录下的 .ts 文件排除在编译上下文之外。
files 用于精确指定需要包含在编译上下文中的文件列表。例如:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
},
"files": ["src/index.ts", "src/utils/api.ts"]
}
只有 src/index.ts 和 src/utils/api.ts 这两个文件会被纳入编译上下文。
类型声明空间用于定义类型相关的内容,如接口、类型别名、枚举等。这些声明只能用于类型注解和类型检查,不能作为值来使用。
// 接口声明
interface User {
name: string;
age: number;
}
// 类型别名声明
type Point = {
x: number;
y: number;
};
// 枚举声明
enum Color {
Red,
Green,
Blue
}
// 使用类型声明
const user: User = { name: 'John', age: 30 };
值声明空间用于定义可以在运行时使用的值,如变量、函数、类等。
// 变量声明
let num = 10;
// 函数声明
function add(a: number, b: number): number {
return a + b;
}
// 类声明
class Person {
constructor(public name: string) {}
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
}
// 使用值声明
const result = add(1, 2);
const person = new Person('Alice');
person.sayHello();
类型和值声明空间的区别:类型声明空间中的声明只能用于类型相关的操作,而值声明空间中的声明可以在运行时被调用或使用。例如,不能将接口作为值来传递或调用。
在 TypeScript 中,模块是一种将代码组织成独立单元的方式,每个模块都有自己的作用域,模块之间可以通过 import 和 export 关键字进行通信。
(1)命名导出:可以导出多个变量、函数、类等,通过名称来引用。
// math.ts
export function add(a: number, b: number) {
return a + b;
}
export function subtract(a: number, b: number) {
return a - b;
}
(2)默认导出:每个模块只能有一个默认导出,使用 export default 关键字。
// person.ts
class Person {
constructor(public name: string) {}
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
}
export default Person;
(1)导入命名导出
// main.ts
import { add, subtract } from './math';
const sum = add(1, 2);
const diff = subtract(5, 3);
(2)导入默认导出
// app.ts
import Person from './person';
const person = new Person('Bob');
person.sayHello();
相对路径解析:使用相对路径来指定模块的位置,如 import { add } from ‘./math’;。
非相对路径解析:根据 tsconfig.json 中的 baseUrl 和 paths 配置来解析模块。例如:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@utils/*": ["src/utils/*"]
}
}
}
import { apiCall } from '@utils/api';
命名空间(以前也称为内部模块)是一种在全局作用域中组织代码的方式,用于避免命名冲突。
// shapes.ts
namespace Shapes {
export class Circle {
constructor(private radius: number) {}
getArea() {
return Math.PI * this.radius * this.radius;
}
}
export class Square {
constructor(private sideLength: number) {}
getArea() {
return this.sideLength * this.sideLength;
}
}
}
// 使用命名空间
const circle = new Shapes.Circle(5);
const square = new Shapes.Square(4);
console.log(circle.getArea());
console.log(square.getArea());
命名空间可以嵌套使用,以进一步组织代码。
namespace Geometry {
export namespace Shapes {
export class Triangle {
constructor(private base: number, private height: number) {}
getArea() {
return 0.5 * this.base * this.height;
}
}
}
}
const triangle = new Geometry.Shapes.Triangle(3, 4);
console.log(triangle.getArea());
作用域:命名空间在全局作用域中定义,而模块有自己独立的作用域。
使用场景:命名空间适用于在一个文件中组织相关的代码,避免全局命名冲突;模块更适合于大型项目,将代码拆分成多个独立的文件进行管理。
动态导入表达式允许在运行时动态地加载模块,而不是在编译时静态地导入。这在需要按需加载模块的场景中非常有用,例如懒加载路由组件。
// 动态导入模块
const module = import('./math');
module.then((math) => {
const sum = math.add(1, 2);
console.log(sum);
});
动态导入返回一个 Promise,因此可以使用 async/await 来处理。
async function loadModule() {
const math = await import('./math');
const diff = math.subtract(5, 3);
console.log(diff);
}
loadModule();
(1)懒加载组件:在单页面应用中,当用户访问某个路由时再动态加载对应的组件,提高应用的加载性能。
// 路由配置
const routes = [
{
path: '/home',
component: () => import('./HomePage')
},
{
path: '/about',
component: () => import('./AboutPage')
}
];
(2)按需加载功能模块:根据用户的操作或特定条件,动态加载所需的功能模块。