万字血书!TypeScript 完全指南

TypeScript 完全指南(大纲)

第1章:TypeScript 简介

1.1 什么是 TypeScript?

TypeScript 是微软开发的开源编程语言,它是 JavaScript 的超集 (Superset),意味着所有合法的 JavaScript 代码都可以直接在 TypeScript 中运行。TypeScript 的核心特性是静态类型检查 ------开发者可以在编码阶段为变量、函数参数、返回值等标注类型,编译器(tsc)会在代码运行前检查类型错误,从而提前发现潜在问题。例如:

typescript 复制代码
function add(a: number, b: number): number {
  return a + b;
}
// 调用时传入非数字会触发编译错误:add("1", 2);

1.2 为什么选择 TypeScript?

  • 类型安全 :减少运行时错误(如 undefined is not a function)。
  • 开发体验:IDE 智能提示、自动补全和重构支持(如 VS Code)。
  • 渐进式:支持逐步迁移,可在现有 JavaScript 项目中局部引入。
  • 生态兼容:编译后的代码是纯 JavaScript,兼容所有浏览器和 Node.js。

1.3 快速上手

  1. 安装环境:

    bash 复制代码
    npm install -g typescript  # 全局安装 TypeScript 编译器
    tsc --version             # 验证安装
  2. 编写第一个程序 :

    创建 hello.ts:

    typescript 复制代码
    let message: string = "Hello, TypeScript!";
    console.log(message);
  3. 编译与运行:

    bash 复制代码
    tsc hello.ts    # 生成 hello.js
    node hello.js   # 输出结果

1.4 TypeScript 的定位

TypeScript 并非替代 JavaScript,而是通过类型系统增强其工程化能力,尤其适合大型项目或团队协作。下一章将深入讲解基础类型与语法。


第2章:基础类型与语法

TypeScript 的基础类型系统通过显式注解增强了代码的可读性和安全性。合理使用这些类型可以大幅减少运行时错误,提升开发效率。

2.1 原始类型(Primitive Types)

TypeScript 的原始类型与 JavaScript 一致,但需要通过类型注解显式声明。

1. string

  • 表示文本数据,可用单引号、双引号或反引号(模板字符串)。

  • 严格类型检查:无法赋值非字符串值。

    typescript 复制代码
    let name: string = "Alice";
    let greeting: string = `Hello, ${name}!`; // 模板字符串
    // name = 100; // 错误:不能将 number 赋值给 string

2. number

  • 包括整数、浮点数、二进制、八进制、十六进制等所有数值类型。

  • 没有独立的 int 或 float ,统一用 number。

    typescript 复制代码
    let decimal: number = 6;
    let hex: number = 0xf00d;     // 十六进制
    let binary: number = 0b1010;  // 二进制
    let infinity: number = Infinity;

3. boolean

  • 仅允许 true 或 false。

  • 禁止隐式转换 :不能直接用 0、"" 等表示假值。

    typescript 复制代码
    let isDone: boolean = false;
    // isDone = ""; // 错误:不能将 string 赋值给 boolean

2.2 数组与元组

1. 数组(Array)

  • 语法 :类型[] 或 Array<类型>。

  • 所有元素必须为同一类型。

    typescript 复制代码
    let numbers: number[] = [1, 2, 3];
    let names: Array<string> = ["Alice", "Bob"];
    // numbers.push("4"); // 错误:类型不匹配

2. 元组(Tuple)

  • 固定长度和类型的数组,适合存储结构化数据。

  • 越界检查:超出定义长度会报错。

    typescript 复制代码
    let user: [string, number] = ["Alice", 30];
    // user = ["Bob", 40, true]; // 错误:长度超过 2
  • 可选元素与剩余元素(TypeScript 4.0+):

    typescript 复制代码
    let option: [string, number?] = ["Alice"];       // 第二个元素可选
    let list: [string, ...number[]] = ["Alice", 1, 2]; // 剩余元素为 number 数组

2.3 枚举(Enum)

  • 用于定义命名常量集合,支持数字或字符串值。

1. 数字枚举

  • 默认从 0 开始自增,可手动赋值。

    typescript 复制代码
    enum Direction { 
      Up = 1,   // 显式赋值
      Down,     // 自动递增为 2
      Left = 10,
      Right     // 自动递增为 11
    }
    console.log(Direction.Up); // 输出: 1

2. 字符串枚举

  • 必须为每个成员显式赋值字符串。

    typescript 复制代码
    enum LogLevel {
      Error = "ERROR",
      Warn = "WARN"
    }

3. 常量枚举(Const Enum)

  • 编译时会被删除,直接替换为常量值,减少代码体积。

    typescript 复制代码
    const enum Size { Small = 10, Medium, Large }
    let size = Size.Medium; // 编译后:let size = 11;

2.4 any、unknown、void、never

1. any

  • 关闭类型检查:可赋值任意类型,慎用(破坏类型安全)。

    typescript 复制代码
    let data: any = "Hello";
    data = 100;       // 合法
    data.toFixed();   // 编译通过,但运行时可能报错!

2. unknown

  • 类型安全的 any:必须通过类型断言或收窄后使用。

    typescript 复制代码
    let input: unknown = fetchUserInput();
    // let name: string = input; // 错误:unknown 不能直接赋值
    if (typeof input === "string") {
      let name: string = input; // 类型收窄后合法
    }

3. void

  • 表示函数没有返回值。

    typescript 复制代码
    function logMessage(msg: string): void {
      console.log(msg);
      // 无 return 语句
    }

4. never

  • 表示函数永远不会返回(如抛出错误或无限循环)。

    typescript 复制代码
    function error(message: string): never {
      throw new Error(message);
    }
    function infiniteLoop(): never {
      while (true) {}
    }

第3章:类型进阶

TypeScript 的类型系统不仅限于基础类型,还提供了强大的工具来描述复杂的数据结构和逻辑。本章将深入讲解接口(Interface) 、联合类型(Union Types) 、类型别名(Type Alias) 、类型断言(Type Assertion) 和 类型守卫(Type Guard) 等高级类型用法。


3.1 接口(Interface)

接口用于定义对象的形状(属性和方法),是 TypeScript 的核心类型工具。

1. 基本用法

typescript 复制代码
interface User {
  name: string;
  age: number;
}

const alice: User = {
  name: "Alice",
  age: 30,
  // isAdmin: true  // 错误:接口未定义 isAdmin
};

2. 可选属性与只读属性

  • 可选属性 :通过 ? 标记。
  • 只读属性 :通过 readonly 标记,初始化后不可修改。
typescript 复制代码
interface Config {
  readonly id: string;  // 只读
  title: string;
  maxRetries?: number;  // 可选
}

const config: Config = {
  id: "123",
  title: "API Config",
};
// config.id = "456";  // 错误:只读属性不可修改

3. 函数类型

接口可以描述函数结构:

typescript 复制代码
interface SearchFunc {
  (source: string, keyword: string): boolean;
}

const search: SearchFunc = (src, kw) => src.includes(kw);

4. 接口继承

通过 extends 实现接口复用:

typescript 复制代码
interface Animal {
  name: string;
}

interface Dog extends Animal {
  bark(): void;
}

const myDog: Dog = {
  name: "Buddy",
  bark: () => console.log("Woof!"),
};

5. 索引签名

描述动态属性名的类型,常用于字典结构:

typescript 复制代码
interface StringArray {
  [index: number]: string;  // 索引为数字,值为字符串
}

const arr: StringArray = ["Alice", "Bob"];
console.log(arr[0]); // 输出: "Alice"

3.2 联合类型与类型别名

1. 联合类型(Union Types)

允许变量为多种类型之一:

typescript 复制代码
type ID = string | number;

function printId(id: ID) {
  console.log(id);
}

printId("ABC123");  // 合法
printId(100);       // 合法

2. 类型别名(Type Alias)

为复杂类型定义简洁的名称:

typescript 复制代码
type Point = {
  x: number;
  y: number;
};

type Callback = (data: string) => void;

3. 联合类型与字面量

结合字面量类型限制取值范围:

typescript 复制代码
type Status = "success" | "error" | "pending";
type Dice = 1 | 2 | 3 | 4 | 5 | 6;

let currentStatus: Status = "success";
let roll: Dice = 4;

3.3 类型断言

手动指定值的类型,适用于开发者比编译器更清楚类型的情况。

1. as 语法

typescript 复制代码
const input = document.getElementById("input") as HTMLInputElement;
input.value = "Hello";  // 断言后可以安全访问 value 属性

2. 非空断言(!)

明确告诉编译器值不为 null/undefined:

typescript 复制代码
function getLength(str?: string) {
  return str!.length;  // 慎用:需确保 str 不为空
}

3.4 类型守卫

通过逻辑判断缩小变量类型范围。

1. typeof 守卫

typescript 复制代码
function printValue(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  } else {
    console.log(value.toFixed(2));
  }
}

2. instanceof 守卫

typescript 复制代码
class Cat { meow() {} }
class Dog { bark() {} }

function handlePet(pet: Cat | Dog) {
  if (pet instanceof Cat) {
    pet.meow();
  } else {
    pet.bark();
  }
}

3. 自定义类型守卫

通过返回值为 value is Type 的函数定义逻辑:

typescript 复制代码
interface Fish { swim(): void }
interface Bird { fly(): void }

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim();
  } else {
    pet.fly();
  }
}

3.5 类型兼容性

TypeScript 使用结构化类型系统(Duck Typing),只要结构匹配即可兼容。

1. 对象兼容性

typescript 复制代码
interface Named { name: string }
let x: Named;
const y = { name: "Alice", age: 30 };
x = y;  // 合法:y 包含 name 属性

2. 函数兼容性

参数类型和返回值类型需兼容:

typescript 复制代码
type Handler = (a: number) => void;

const handler1: Handler = (a: number) => {};
const handler2: Handler = (a: number, b: string) => {};  // 错误:参数数量不兼容

第4章:函数与类

TypeScript 在函数和类的定义中引入了严格的类型系统,使得面向对象编程更加安全和可维护。本章将详细讲解函数类型、类的基本语法、继承机制、访问修饰符等核心内容,并深入探讨抽象类与接口的应用。


4.1 函数类型

1. 函数参数与返回值的类型注解

  • 参数类型:显式声明参数类型。

  • 返回值类型:可显式标注返回值类型,编译器会自动推断。

    typescript 复制代码
    function add(a: number, b: number): number {
      return a + b;
    }
    // 箭头函数类型
    const multiply = (x: number, y: number): number => x * y;

2. 可选参数与默认参数

  • 可选参数 :通过 ? 标记,必须位于必选参数之后。

  • 默认参数:直接赋值默认值,类型可自动推断。

    typescript 复制代码
    function greet(name: string, greeting: string = "Hello", suffix?: string): string {
      return `${greeting}, ${name}${suffix ? " " + suffix : ""}!`;
    }
    greet("Alice"); // 输出: "Hello, Alice!"
    greet("Bob", "Hi", "Mr."); // 输出: "Hi, Bob Mr.!"

3. 剩余参数(Rest Parameters)

  • 将多个参数收集为数组,类型必须为数组。

    typescript 复制代码
    function sum(...numbers: number[]): number {
      return numbers.reduce((acc, curr) => acc + curr, 0);
    }
    sum(1, 2, 3); // 输出: 6

4. 函数重载(Function Overloads)

  • 为同一函数提供多个类型定义,解决多态场景的类型问题。

    typescript 复制代码
    // 重载签名
    function formatInput(input: string): string;
    function formatInput(input: number): string;
    
    // 实现签名(需兼容所有重载)
    function formatInput(input: string | number): string {
      return typeof input === "string" ? input.trim() : input.toFixed(2);
    }
    
    formatInput("  text  "); // 返回 "text"
    formatInput(3.1415);     // 返回 "3.14"

4.2 类的类型化

1. 类的基本语法

  • 属性类型 :必须在类中显式声明类型(除非通过构造函数赋值且启用 strictPropertyInitialization)。

  • 构造函数 :使用 constructor 定义。

    typescript 复制代码
    class Person {
      name: string;
      age: number;
    
      constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
      }
    
      sayHello(): string {
        return `Hello, I'm ${this.name}`;
      }
    }
    
    const alice = new Person("Alice", 30);

2. 访问修饰符(Access Modifiers)

  • public:默认修饰符,可在任何地方访问。

  • private:仅类内部访问。

  • protected:类内部和子类中访问。

    typescript 复制代码
    class Animal {
      public name: string;
      private secret: string = "hidden";
      protected age: number;
    
      constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
      }
    }
    
    class Dog extends Animal {
      bark() {
        console.log(this.age);  // 合法:protected 属性

第5章:泛型(Generics)

泛型是 TypeScript 中用于编写可复用、灵活且类型安全的代码的核心工具。它允许开发者定义函数、类或接口时不预先指定具体类型,而是在使用时动态指定类型,从而避免重复代码并保持类型约束。本章将深入讲解泛型的使用场景、语法和高级技巧。


5.1 泛型的基本概念

1. 为什么需要泛型?

假设需要编写一个函数,返回传入的任意类型的参数:

typescript 复制代码
// 无泛型:使用 any 会丢失类型信息
function identity(arg: any): any {
  return arg;
}
const num = identity(100); // num 的类型为 any,无法推断为 number

使用泛型可以保留类型信息:

typescript 复制代码
function identity<T>(arg: T): T {
  return arg;
}
const num = identity<number>(100); // num 类型为 number
const str = identity("Hello");     // 类型自动推断为 string

2. 泛型语法

  • 类型参数 :使用 <T> 定义泛型参数(T 是约定俗成的名称,可自定义)。
  • 动态绑定:调用时显式指定类型或由编译器自动推断。

5.2 泛型函数

1. 基本用法

typescript 复制代码
function logAndReturn<T>(value: T): T {
  console.log(value);
  return value;
}

logAndReturn<string>("Hello"); // 显式指定类型
logAndReturn(42);              // 自动推断为 number

2. 多类型参数

可定义多个泛型参数,用逗号分隔:

typescript 复制代码
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

const result = swap(["Alice", 30]); // 类型推断为 [number, string]

5.3 泛型接口

1. 接口中的泛型

定义可复用的通用结构:

typescript 复制代码
interface KeyValuePair<K, V> {
  key: K;
  value: V;
}

const pair1: KeyValuePair<string, number> = { key: "age", value: 30 };
const pair2: KeyValuePair<number, boolean> = { key: 1, value: true };

2. 函数类型泛型

描述通用函数结构:

typescript 复制代码
interface GenericFunc<T> {
  (arg: T): T;
}

const numericFunc: GenericFunc<number> = (x) => x * 2;
numericFunc(10); // 返回 20

5.4 泛型类

1. 类中的泛型

用于创建可处理多种类型的容器类:

typescript 复制代码
class Box<T> {
  private content: T;

  constructor(value: T) {
    this.content = value;
  }

  getValue(): T {
    return this.content;
  }
}

const numberBox = new Box<number>(100);
const stringBox = new Box("Hello"); // 自动推断为 Box<string>

2. 泛型约束

限制泛型参数的类型范围,使用 extends 关键字:

typescript 复制代码
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): void {
  console.log(arg.length);
}

logLength("Hello"); // 合法,string 有 length 属性
logLength([1, 2, 3]); // 合法,数组有 length
// logLength(100);    // 错误:number 没有 length

3. keyof 约束

确保访问对象属性的安全性:

typescript 复制代码
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Alice", age: 30 };
getProperty(user, "name"); // 返回 "Alice"
// getProperty(user, "email"); // 错误:email 不是 user 的属性

5.5 泛型工具类型

TypeScript 内置了一些泛型工具类型,简化常见类型操作。

1. Partial<T>

将类型 T 的所有属性变为可选:

typescript 复制代码
interface User {
  name: string;
  age: number;
}

type PartialUser = Partial<User>;
// 等效于 { name?: string; age?: number; }

2. Readonly<T>

将类型 T 的所有属性变为只读:

typescript 复制代码
type ReadonlyUser = Readonly<User>;
// 等效于 { readonly name: string; readonly age: number; }

3. Pick<T, K>

从类型 T 中选择部分属性 K:

typescript 复制代码
type UserName = Pick<User, "name">; // { name: string }

4. Record<K, T>

定义键为 K、值为 T 的对象类型:

typescript 复制代码
type UserMap = Record<string, User>;
// 等效于 { [key: string]: User }

5.6 泛型默认类型

为泛型参数提供默认值,简化调用时的类型声明:

typescript 复制代码
interface ResponseData<T = string> {
  code: number;
  data: T;
}

const stringResponse: ResponseData = { code: 200, data: "OK" }; // T 默认为 string
const numberResponse: ResponseData<number> = { code: 200, data: 100 };

5.7 泛型的实际应用

1. API 响应类型

定义通用的 API 响应结构:

typescript 复制代码
interface ApiResponse<T> {
  success: boolean;
  data: T;
  error?: string;
}

// 用户数据接口
interface UserData { id: string; name: string }

// 具体调用
const userResponse: ApiResponse<UserData> = await fetchUser();

2. React 组件 Props

在 React 中定义泛型组件:

typescript 复制代码
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return <div>{items.map(renderItem)}</div>;
}

// 使用示例
<List<number> items={[1, 2, 3]} renderItem={(n) => <div>{n}</div>} />;

第6章:模块与命名空间

模块(Module)和命名空间(Namespace)是 TypeScript 中管理代码作用域、组织复杂项目的核心机制。它们帮助开发者避免全局命名冲突,实现代码的模块化和复用。本章将深入探讨模块的导入导出、命名空间的设计模式,以及如何结合现代工具构建可维护的代码结构。


6.1 模块(Modules)

1. 模块的基本概念

  • 模块化编程 :将代码拆分为独立的文件,每个文件是一个模块,通过 import/export 管理依赖。
  • 作用域隔离:模块内的变量、函数、类默认在模块作用域内,不污染全局。

2. ES Module 语法

  • 导出(Export) :

    typescript 复制代码
    // math.ts
    export const PI = 3.14;                  // 导出常量
    export function sum(a: number, b: number): number {  // 导出函数
      return a + b;
    }
    export class Calculator {                // 导出类
      static multiply(a: number, b: number): number {
        return a * b;
      }
    }
    
    // 默认导出(每个模块仅一个)
    export default class AdvancedCalculator extends Calculator {}
  • 导入(Import) :

    typescript 复制代码
    // app.ts
    import { PI, sum } from "./math";        // 命名导入
    import AdvancedCalculator from "./math"; // 默认导入
    import * as MathUtils from "./math";     // 全部导入为命名空间
    
    console.log(PI);                         // 3.14
    console.log(sum(2, 3));                  // 5
    const calc = new AdvancedCalculator();

3. 模块编译与配置

  • 模块类型 :在 tsconfig.json 中指定模块系统(如 "module": "ES2020" 或 "CommonJS")。
  • 文件扩展名 :TypeScript 支持 .ts、.tsx、.mts(ES 模块)和 .cts(CommonJS 模块)。

6.2 命名空间(Namespaces)

1. 命名空间的作用

  • 逻辑分组:将相关功能组织在单一命名空间内,避免全局污染。
  • 兼容性:适用于旧代码或浏览器环境(现代项目推荐使用模块)。

2. 定义与使用

typescript 复制代码
// utilities.ts
namespace Utilities {
  export function log(msg: string): void {  // 导出才能在外部访问
    console.log(msg);
  }

  export namespace MathHelper {             // 嵌套命名空间
    export const PI = 3.14;
  }
}

// 使用命名空间
Utilities.log("Hello");                     // 输出: Hello
console.log(Utilities.MathHelper.PI);       // 输出: 3.14

3. 多文件命名空间

通过 /// <reference path="..." /> 合并多个文件的命名空间:

  • file1.ts:

    typescript 复制代码
    namespace Utilities {
      export function log(msg: string) { ... }
    }
  • file2.ts:

    typescript 复制代码
    /// <reference path="file1.ts" />
    namespace Utilities {
      export function debug(msg: string) { ... }
    }
  • 编译后 :两个文件的 Utilities 命名空间会被合并。


6.3 模块与命名空间的对比

特性 模块 命名空间
作用域 文件作用域 全局或命名空间作用域
依赖管理 显式 import/export 隐式通过命名空间引用
适用场景 现代浏览器/Node.js 项目 旧项目或浏览器环境
编译输出 根据配置生成 ES/CommonJS 模块 合并为全局对象或 IIFE
推荐使用 ✅ 优先选择模块 ⚠️ 仅在必要时使用

6.4 模块解析策略

1. 解析方式

  • 相对路径 :import "./math" 解析为当前目录下的文件。
  • 非相对路径 :import "lodash" 从 node_modules 或配置的路径解析。

2. 路径别名配置

在 tsconfig.json 中配置 paths 简化导入路径:

json 复制代码
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@utils/*": ["utils/*"]  // 将 @utils/ 映射到 src/utils/
    }
  }
}

使用示例:

typescript 复制代码
import { log } from "@utils/logger";  // 实际路径: src/utils/logger.ts

6.5 声明文件(.d.ts)

1. 声明文件的作用

为第三方 JavaScript 库或全局变量提供类型定义,增强 TypeScript 支持。

2. 声明全局模块

typescript 复制代码
// global.d.ts
declare module "jquery" {
  export function $(selector: string): any;
  export namespace $ {
    function ajax(url: string): void;
  }
}

3. 声明全局变量

typescript 复制代码
declare const VERSION: string;  // 声明全局常量
declare interface Window {
  MY_APP_CONFIG: Record<string, any>;  // 扩展全局 Window 类型
}

6.6 模块的进阶用法

1. 动态导入(Dynamic Import)

按需加载模块,返回 Promise:

typescript 复制代码
async function loadModule() {
  const math = await import("./math");
  console.log(math.sum(1, 2));
}

2. 重新导出(Re-export)

聚合多个模块的导出:

typescript 复制代码
// utils/index.ts
export { log } from "./logger";
export { encrypt } from "./crypto";

3. 模块的副作用

执行模块代码但不导入任何内容:

typescript 复制代码
import "./polyfills";  // 执行 polyfills.ts 中的初始化代码

相关推荐
卸任5 小时前
Vibe coding工具使用心得
前端·ai编程
brien96 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance6 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M6 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写6 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李6 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易7 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马7 小时前
前沿视界
前端
kyriewen7 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易7 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员