注:纯手打,如有错误欢迎评论区交流!
转载请注明出处:https://blog.youkuaiyun.com/testleaf/article/details/148275110
编写此文是为了更好地学习前端知识,如果损害了有关人的利益,请联系删除!
本文章将不定时更新,敬请期待!!!
欢迎点赞、收藏、转发、关注,多谢!!!
一、TypeScript和JavaScript对比
- 选择 TypeScript:
- 适合中大型项目、长期维护的代码库或需要强类型保障的场景。
- 示例:React/Angular 应用、Node.js 后端服务。
- 选择 JavaScript:
- 适合快速原型开发、小型脚本或已有成熟 JS 代码库。
- 示例:简单的网页交互、一次性脚本。
- 关键差异一句话:
- TypeScript = JavaScript + 静态类型系统 + 开发时工具增强,牺牲少量灵活性换取长期可维护性。
二、编译TypeScript
1、基础编译流程
步骤 1:安装 TypeScript 编译器
npm install -g typescript # 全局安装
# 或
npm install --save-dev typescript # 项目内安装
步骤 2:创建 tsconfig.json
运行以下命令生成默认配置:
tsc --init
或手动创建配置文件(示例):
{
"compilerOptions": {
"target": "ES6", // 编译目标 JS 版本
"module": "CommonJS", // 模块系统
"outDir": "./dist", // 输出目录
"strict": true // 启用严格类型检查
},
"include": ["src/**/*"] // 需编译的文件路径
}
步骤 3:执行编译
tsc # 编译所有文件(根据 tsconfig.json)
tsc src/index.ts # 编译单个文件(忽略 tsconfig.json)
2、关键编译配置项
配置项 | 作用 | 常用值 |
---|---|---|
target | 指定输出的 JS 版本 | ES5, ES6, ES2022 |
module | 模块化方案 | CommonJS, ES6, AMD |
outDir | 编译后的 JS 文件输出目录 | “./dist” |
strict | 启用所有严格类型检查(推荐开启) | true/false |
noImplicitAny | 禁止隐式 any 类型(需显式标注) | true |
sourceMap | 生成 .map 文件以支持调试原始 TS 代码 | true |
esModuleInterop | 改善 CommonJS/ES Module 的兼容性 | true |
3、编译模式
实时监听模式
tsc --watch # 自动重新编译修改的文件
增量编译
tsc --incremental # 仅编译变更部分(加快后续编译速度)
4、与构建工具集成【如Webpack】
安装 ts-loader:
npm install --save-dev ts-loader
修改 webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
};
5、Vite/Rollup
直接支持 TypeScript,无需额外配置(需确保 tsconfig.json 存在)。
6、总结
- 核心工具:
tsc
(TypeScript 编译器) +tsconfig.json
。 - 推荐实践:开启
strict
严格模式,配合--watch
或构建工具实现自动化。 - 扩展能力:通过 Babel 支持最新语法,或与 Webpack/Vite 等工具链集成。
三、类型声明
1、基础类型声明
原始类型
let name: string = "Alice";
let age: number = 25;
let isActive: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;
let bigInt: bigint = 100n;
let symbolKey: symbol = Symbol("id");
数组与元组
let numbers: number[] = [1, 2, 3]; // 数字数组
let mixed: (string | number)[] = ["a", 1]; // 联合类型数组
let tuple: [string, number] = ["Alice", 25]; // 固定长度和类型的元组
2、对象与接口
接口(Interface)
定义对象结构的首选方式:
interface User {
id: number;
name: string;
email?: string; // 可选属性
readonly createdAt: Date; // 只读属性
}
const user: User = {
id: 1,
name: "Alice",
createdAt: new Date(),
};
类型别名(Type Alias)
适用于复杂类型或联合类型:
type Point = {
x: number;
y: number;
};
type ID = string | number; // 联合类型
区别:Interface vs Type
特性 | Interface | Type Alias |
---|---|---|
扩展性 | ✅ 可通过 extends 继承 | ✅ 使用交叉类型(& ) |
合并声明 | ✅ 同名接口自动合并 | ❌ 不允许重复声明 |
适用场景 | 对象结构、类实现 | 联合类型、元组、复杂类型表达式 |
3、函数类型声明
函数参数与返回值
// 方式1:直接标注
function add(a: number, b: number): number {
return a + b;
}
// 方式2:类型别名
type AddFn = (a: number, b: number) => number;
const add2: AddFn = (a, b) => a + b;
可选参数与默认值
function greet(name: string, prefix: string = "Hello"): string {
return `${prefix}, ${name}!`;
}
剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((acc, n) => acc + n, 0);
}
4、高级类型
联合类型(Union Types)
type Status = "success" | "error" | "pending";
let currentStatus: Status = "success";
交叉类型(Intersection Types)
type Admin = { role: "admin" };
type User = { name: string };
type AdminUser = Admin & User; // { role: "admin", name: string }
泛型(Generics)
function identity<T>(value: T): T {
return value;
}
identity<string>("Alice"); // 显式指定类型
identity(42); // 自动推断为 number
工具类型(Utility Types)
interface Todo {
title: string;
completed: boolean;
}
type PartialTodo = Partial<Todo>; // 所有属性变为可选
type ReadonlyTodo = Readonly<Todo>; // 所有属性变为只读
type TodoPreview = Pick<Todo, "title">; // 仅保留指定属性
5、类型断言与守卫
类型断言(Type Assertion)
明确告诉编译器值的类型:
let input: unknown = "hello";
let length: number = (input as string).length; // 方式1
let length2: number = (<string>input).length; // 方式2(JSX 中不可用)
类型守卫(Type Guards)
运行时检查类型:
function isString(value: unknown): value is string {
return typeof value === "string";
}
if (isString(input)) {
console.log(input.toUpperCase()); // 此处 input 被识别为 string
}
6、模块与全局类型
模块内类型导出
// types.ts
export interface Product {
id: number;
name: string;
}
// app.ts
import { Product } from "./types";
全局类型声明
// global.d.ts
declare type GlobalConfig = {
env: "dev" | "prod";
};
// 任何文件可直接使用 GlobalConfig
const config: GlobalConfig = { env: "dev" };
7、最佳实践
- 始终开启 strict 模式。
- 优先用 interface 定义对象,用 type 处理联合/交叉类型。
- 泛型提升代码复用性,工具类型减少重复定义。
四、类型总览
1、原始类型(Primitive Types)
let str: string = "Hello";
let num: number = 42;
let bool: boolean = true;
let nul: null = null;
let undef: undefined = undefined;
let big: bigint = 9007199254740991n;
let sym: symbol = Symbol("key");
特点:对应 JavaScript 的 7 种原始数据类型。
2、对象类型(Object Types)
数组
let arr1: number[] = [1, 2, 3];
let arr2: Array<string> = ["a", "b"]; // 泛型语法
let arr3: (number | string)[] = [1, "a"]; // 联合类型数组
元组(Tuple)
let tuple: [string, number] = ["Alice", 25]; // 固定长度和类型
tuple.push("extra"); // 允许但不推荐(破坏元组约束)
对象字面量
let obj: {
name: string;
age?: number; // 可选属性
readonly id: number; // 只读属性
} = { name: "Alice", id: 1 };
3、特殊类型
类型 | 说明 | 示例 |
---|---|---|
any | 禁用类型检查 | let val: any = "anything"; |
unknown | 类型安全的 any,需类型断言或守卫后才能使用 | let u: unknown = fetchData(); |
void | 表示无返回值(常见于函数) | function log(): void { ... } |
never | 表示永不返回(如抛出错误或死循环) | function fail(): never { throw Error(); } |
enum | 枚举类型 | enum Color { Red, Green } |
4、高级类型
联合类型(Union)
type Status = "success" | "error" | "pending";
let status: Status = "success";
function padLeft(value: string, padding: string | number) { ... }
交叉类型(Intersection)
type Admin = { permissions: string[] };
type User = { name: string };
type AdminUser = Admin & User; // { permissions: string[], name: string }
索引类型
interface StringArray {
[index: number]: string; // 数字索引签名
}
const arr: StringArray = ["a", "b"];
映射类型(Mapped Types)
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Optional<T> = { [P in keyof T]?: T[P] };
5、泛型(Generics)
基础泛型
function identity<T>(arg: T): T {
return arg;
}
identity<string>("text"); // 显式指定类型
identity(42); // 自动推断为 number
泛型约束
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): void {
console.log(arg.length);
}
泛型工具类
type Partial<T> = { [P in keyof T]?: T[P] }; // 所有属性可选
type Pick<T, K extends keyof T> = { [P in K]: T[P] }; // 选取部分属性
6、条件类型(Conditional Types)
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<number>; // false
内置条件类型
type Exclude<T, U> = T extends U ? never : T; // 从 T 中排除 U
type Extract<T, U> = T extends U ? T : never; // 从 T 中提取 U
7、类型操作
类型推断(infer)
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type FnReturn = ReturnType<() => number>; // number
模板字面量类型
type EventName = `on${"Click" | "Hover"}`; // "onClick" | "onHover"
8、类型声明合并
接口合并
interface Box { width: number; }
interface Box { height: number; }
// 合并为 { width: number; height: number; }
命名空间合并
namespace Network {
export function request() {}
}
namespace Network {
export function response() {}
}
// 合并后包含 request 和 response 方法
9、最佳实践
- 优先使用 interface 定义对象结构,用 type 处理复杂类型操作。
- 避免 any,用 unknown + 类型守卫保证类型安全。
- 善用工具类型(如 Partial, Pick)减少重复代码。
- 泛型提升代码复用性,但避免过度抽象。
- 开启 strict 模式 最大化类型检查收益。
五、类
1、基础类定义
class Person {
// 属性声明(需类型注解)
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
const alice = new Person("Alice", 25);
console.log(alice.greet()); // "Hello, I'm Alice"
2、访问修饰符
修饰符 | 作用 | 示例 |
---|---|---|
public | 默认值,任意位置可访问 | public name: string; |
private | 仅类内部可访问 | private secret: string; |
protected | 类内部及子类可访问 | protected id: number; |
readonly | 只读属性(需初始化) | readonly id: number; |
class Employee extends Person {
private salary: number;
constructor(name: string, age: number, salary: number) {
super(name, age);
this.salary = salary;
}
getSalary(): number {
return this.salary; // 允许访问 private 属性
}
}
const emp = new Employee("Bob", 30, 5000);
console.log(emp.name); // ✅ 允许(public)
console.log(emp.salary); // ❌ 错误(private)
3、参数属性(简写语法)
将构造函数参数直接定义为属性:
class Point {
constructor(
public x: number,
public y: number,
private readonly id: string
) {}
}
const p = new Point(10, 20, "p1");
console.log(p.x); // 10(自动成为 public 属性)
4、Getter/Setter
class Temperature {
private _celsius: number = 0;
get celsius(): number {
return this._celsius;
}
set celsius(value: number) {
if (value < -273.15) throw new Error("Too cold!");
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 1.8 + 32;
}
}
const temp = new Temperature();
temp.celsius = 25; // 调用 setter
console.log(temp.fahrenheit); // 77(调用 getter)
5、静态成员
class Logger {
static instance: Logger;
private logs: string[] = [];
private constructor() {} // 私有构造函数(单例模式)
static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
log(message: string): void {
this.logs.push(message);
console.log(message);
}
}
// 使用单例
Logger.getInstance().log("System started");
6、抽象类
abstract class Animal {
abstract makeSound(): void; // 抽象方法(需子类实现)
move(): void {
console.log("Moving...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof!");
}
}
const dog = new Dog();
dog.makeSound(); // "Woof!"
7、类与接口
实现接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date): void {
this.currentTime = d;
}
}
类类型接口
interface ClockConstructor {
new (hour: number, minute: number): ClockInstance;
}
interface ClockInstance {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
h: number,
m: number
): ClockInstance {
return new ctor(h, m);
}
class DigitalClock implements ClockInstance {
constructor(h: number, m: number) {}
tick() {
console.log("beep beep");
}
}
const digital = createClock(DigitalClock, 12, 30);
8、高级特性
装饰器(实验性)
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${key} with`, args);
return originalMethod.apply(this, args);
// 或者:return originalMethod.call(this, ...args);
};
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
泛型类
class Box<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
const stringBox = new Box<string>("Hello");
const numberBox = new Box<number>(42);
类与类型的关系
class Car {
model: string = "";
}
// 类本身作为类型
const car1: Car = new Car();
// 类类型包含静态部分和实例部分
typeof Car; // 构造函数类型
Car.prototype; // 实例原型
9、最佳实践
- 优先使用 private 封装内部状态,通过方法暴露必要操作。
- 避免过度继承,组合优于继承(使用接口定义行为)。
- 抽象类适合定义通用模板,接口适合定义契约。
- 开启 strictPropertyInitialization 确保属性初始化安全。
- 示例项目结构建议:
// interfaces/vehicle.ts
export interface Vehicle {
start(): void;
stop(): void;
}
// classes/Car.ts
import { Vehicle } from "../interfaces/vehicle";
export class Car implements Vehicle {
start() { /* ... */ }
stop() { /* ... */ }
}
六、类型声明文件
在 TypeScript 中,类型声明文件(.d.ts 文件) 用于描述 JavaScript 库或模块的类型信息,使 TypeScript 能对非 TS 代码进行类型检查。
1、声明文件的作用
- 为 JS 库提供类型支持(如 jQuery、Lodash)
- 描述全局变量/模块的类型
- 扩展已有类型定义
- 避免在业务代码中混入类型声明
2、声明文件的分类
全局类型声明(无导入导出)
// global.d.ts
declare const __VERSION__: string; // 全局变量
declare interface Window {
myLib: { version: string }; // 扩展全局接口
}
declare module "*.png" { // 模块声明
const path: string;
export default path;
}
模块类型声明(含导入导出)
// types/module.d.ts
declare module "my-library" {
export function doSomething(config: { timeout: number }): void;
}
第三方库类型(@types/xxx)
通过 DefinitelyTyped 安装:
npm install --save-dev @types/lodash
3、核心语法
变量/函数声明
declare const PI: number;
declare function greet(name: string): void;
类型/接口声明
declare type UserID = string | number;
declare interface User {
id: UserID;
name: string;
}
命名空间(模拟旧版模块)
declare namespace MyLib {
export const version: string;
export function parse(text: string): object;
}
扩展已有声明
// 扩展 Express 的 Request 类型
declare namespace Express {
interface Request {
user?: { id: string };
}
}
4、声明文件的存放位置
场景 | 推荐路径 | 说明 |
---|---|---|
全局类型 | ./types/global.d.ts | 自动被 TS 识别(需在 tsconfig.json 的 "include" 中包含该目录) |
模块补充类型 | ./types/module-name.d.ts | 通过 declare module 扩展模块类型(如为第三方库添加类型声明) |
第三方库类型 | node_modules/@types/ | 自动从 @types/ 加载(通过 npm install @types/包名 安装) |
配置 tsconfig.json:
{
"compilerOptions": {
"typeRoots": ["./types", "./node_modules/@types"]
},
"include": ["src/**/*", "types/**/*"]
}
5、自动生成声明文件
为 TS 项目生成 .d.ts
在 tsconfig.json 中配置:
{
"compilerOptions": {
"declaration": true,
"outDir": "dist/types"
}
}
编译后将生成对应的声明文件(如 dist/types/index.d.ts)。
从 JS 代码生成(JSDoc 注释)
通过 allowJs 和 declaration 选项:
{
"compilerOptions": {
"allowJs": true,
"declaration": true
}
}
6、常见场景示例
为 Legacy JS 库添加类型
// types/old-lib.d.ts
declare module "old-lib" {
export function calculate(a: number, b: number): number;
export const DEFAULT_TIMEOUT: number;
}
扩展 Vue 组件选项
// types/vue.d.ts
import Vue from "vue";
declare module "vue/types/options" {
interface ComponentOptions<V extends Vue> {
customOption?: string;
}
}
定义 CSS Modules 类型
// types/css-modules.d.ts
declare module "*.module.css" {
const classes: { [key: string]: string };
export default classes;
}
7、最佳实践
- 优先使用
@types/
包:- 在 DefinitelyTyped 已存在类型定义时直接安装。
- 最小化全局声明:
- 尽量通过模块声明(declare module)而非全局变量。
- 类型合并优于覆盖:
- 使用 interface 扩展而非重新声明。
- 保持与源码同步:
- 当修改 JS 代码时,需同步更新声明文件。
- 验证声明文件:
- 通过
tsc --noEmit
检查类型是否正确定义。
- 通过
8、调试技巧
检查类型定义
// test.ts
import { someFunction } from "your-library";
someFunction("test"); // 触发类型检查
快速定位问题
- 使用 VS Code 的 Go to Definition 跳转到声明文件
- 通过
tsc --traceResolution
查看类型解析过程
七、装饰器
1、启用装饰器
在 tsconfig.json 中启用实验性支持:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true // 可选:生成反射元数据
}
}
2、装饰器类型
类装饰器
应用于类的构造函数,用于修改或替换类定义:
function LogClass(target: Function) {
console.log(`Class ${target.name} 被定义`);
}
@LogClass
class MyClass {} // 输出: "Class MyClass 被定义"
动态扩展类:
function AddMethod(target: Function) {
target.prototype.newMethod = () => console.log("动态添加的方法");
}
@AddMethod
class Demo {}
const obj = new Demo();
(obj as any).newMethod(); // 输出: "动态添加的方法"
方法装饰器
修改类方法的行为(如日志、拦截等):
function LogMethod(
target: any,
methodName: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`调用方法 ${methodName},参数:`, args);
return originalMethod.apply(this, args);
};
}
class Calculator {
@LogMethod
add(a: number, b: number) {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // 输出日志后返回 5
属性装饰器
用于监听属性访问或修改:
function DefaultValue(value: any) {
return (target: any, propertyKey: string) => {
let currentValue = value;
Object.defineProperty(target, propertyKey, {
get: () => currentValue,
set: (newValue) => {
console.log(`属性 ${propertyKey} 从 ${currentValue} 变为 ${newValue}`);
currentValue = newValue;
},
});
};
}
class User {
@DefaultValue("匿名")
name: string;
}
const user = new User();
console.log(user.name); // "匿名"
user.name = "Alice"; // 输出变更日志
参数装饰器
主要用于依赖注入(如 Angular)或参数验证:
function CheckParam(target: any, methodName: string, paramIndex: number) {
console.log(`方法 ${methodName} 的第 ${paramIndex} 个参数被装饰`);
}
class Validator {
validate(@CheckParam input: string) {}
// 输出: "方法 validate 的第 0 个参数被装饰"
}
3、装饰器工厂
通过高阶函数实现可配置的装饰器:
function LogWithPrefix(prefix: string) {
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`[${prefix}] 调用方法 ${methodName}`);
return originalMethod.apply(this, args);
};
};
}
class Service {
@LogWithPrefix("DEBUG")
fetchData() {
return "数据";
}
}
4、装饰器执行顺序
- 参数装饰器 → 方法/属性装饰器 → 类装饰器
- 同类型装饰器从下到上执行(针对方法/属性)或从上到下执行(针对类)。
5、实际应用场景
AOP 编程(日志/性能监控)
function MeasureTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = performance.now();
const result = originalMethod.apply(this, args);
console.log(`${methodName} 执行耗时: ${performance.now() - start}ms`);
return result;
};
}
依赖注入(DI)
function Injectable(target: Function) {
// 注册到 IoC 容器
Container.register(target);
}
@Injectable
class DatabaseService {}
路由绑定(如 Express/NestJS)
function Get(path: string) {
return (target: any, methodName: string) => {
Router.register("GET", path, target[methodName]);
};
}
class UserController {
@Get("/users")
listUsers() { /* ... */ }
}
6、注意事项
- 实验性特性:未来标准可能变化,生产环境建议结合框架使用(如 Angular/NestJS)。
- 静态限制:装饰器无法修改类/方法的静态类型信息(需配合类型断言或泛型)。
- 性能影响:过度使用装饰器可能增加启动时间。