TypeScript知识点梳理

注:纯手打,如有错误欢迎评论区交流!
转载请注明出处: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​

特性InterfaceType 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)。
  • ​​静态限制​​:装饰器无法修改类/方法的静态类型信息(需配合类型断言或泛型)。
  • ​​性能影响​​:过度使用装饰器可能增加启动时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

testleaf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值