TypeScript 简介

TypeScript 是 JavaScript 的超集,添加了静态类型定义和其他高级特性。它由微软开发和维护,旨在提高大型应用程序的开发效率和代码质量。TypeScript 代码最终会被编译成纯 JavaScript,因此可以在任何支持 JavaScript 的平台上运行。

TypeScript 基本结构

一个简单的 TypeScript 文件示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 定义类型
interface User {
name: string;
age: number;
isActive: boolean;
}

// 使用类型
function greetUser(user: User): string {
return `你好,${user.name}!你今年 ${user.age} 岁了。`;
}

// 创建符合类型的对象
const newUser: User = {
name: "张三",
age: 28,
isActive: true,
};

// 调用函数
console.log(greetUser(newUser));

TypeScript 的编译过程

TypeScript 代码需要编译成 JavaScript 才能运行:

1
2
3
4
5
6
7
8
# 安装 TypeScript
npm install -g typescript

# 编译 TypeScript 文件
tsc main.ts

# 运行编译后的 JavaScript
node main.js

推荐的 TypeScript 学习资源

课程推荐:

TypeScript 官方文档:🛫
W3Schools:🛫

在线练习:
TypeScript Playground:🛫

❗❗❗ 学习重点 🤔:

  1. 基本类型系统

    • 原始类型

      • number:所有数值类型,包括整数和浮点数
      • string:文本字符串类型
      • boolean:真/假值(true/false)
      • nullundefined:表示空值或未定义
      • symbol:唯一且不可变的值,常用作对象属性的键
      • bigint:表示任意精度的整数
    • 复杂类型

      • object:表示所有非原始类型
      • Array<T>T[]:数组类型,如 number[]Array<string>
      • tuple:固定长度的数组,每个位置可以有不同类型,如 [string, number]
      • enum:枚举类型,为一组数值赋予有名字的常量
    • 特殊类型

      • any:任意类型,跳过类型检查(应谨慎使用)
      • unknown:未知类型,比 any 更安全,需要类型检查后才能使用
      • void:通常用于表示函数没有返回值
      • never:表示永远不会发生的值的类型
    • 类型注解与推断

      1
      2
      3
      4
      5
      // 类型注解
      let name: string = "张三";

      // 类型推断
      let age = 25; // 自动推断为 number 类型
  2. 接口与类型别名

    • 接口定义

      1
      2
      3
      4
      5
      interface Person {
      name: string;
      age: number;
      sayHello(): void;
      }
    • 可选与只读属性

      1
      2
      3
      4
      5
      interface Config {
      readonly id: string; // 只读属性
      name: string;
      description?: string; // 可选属性
      }
    • 类型别名

      1
      2
      3
      4
      5
      6
      type Point = {
      x: number;
      y: number;
      };

      type ID = string | number; // 联合类型
    • 接口与类型别名的区别

      • 接口可以被扩展和实现,适用于描述对象形状
      • 类型别名更适合复杂类型组合,如联合类型、交叉类型等
      • 接口可以合并声明,类型别名不能
  3. 函数类型

    • 函数类型定义

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 函数类型注解
      function add(a: number, b: number): number {
      return a + b;
      }

      // 函数类型表达式
      let calculate: (x: number, y: number) => number;

      // 接口定义函数类型
      interface MathFunc {
      (a: number, b: number): number;
      }
    • 可选参数与默认值

      1
      2
      3
      4
      5
      6
      7
      function greet(name: string, greeting?: string): string {
      return `${greeting || "你好"}, ${name}!`;
      }

      function multiply(a: number, b: number = 1): number {
      return a * b;
      }
    • 剩余参数

      1
      2
      3
      function sum(...numbers: number[]): number {
      return numbers.reduce((total, n) => total + n, 0);
      }
    • 函数重载

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 重载签名
      function process(x: number): number;
      function process(x: string): string;

      // 实现
      function process(x: number | string): number | string {
      if (typeof x === "number") {
      return x * 2;
      } else {
      return x.repeat(2);
      }
      }
  4. 类与面向对象编程

    • 类的基本结构

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      class Person {
      // 属性
      name: string;

      // 构造函数
      constructor(name: string) {
      this.name = name;
      }

      // 方法
      greet(): string {
      return `你好,我是 ${this.name}`;
      }
      }
    • 访问修饰符

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class Employee {
      public name: string; // 公共,默认
      private salary: number; // 私有,仅在类内可访问
      protected department: string; // 受保护,在类内和子类可访问
      readonly id: number; // 只读属性

      constructor(
      name: string,
      salary: number,
      department: string,
      id: number
      ) {
      this.name = name;
      this.salary = salary;
      this.department = department;
      this.id = id;
      }
      }
    • 继承与实现接口

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      // 继承
      class Manager extends Employee {
      reports: Employee[];

      constructor(
      name: string,
      salary: number,
      department: string,
      id: number
      ) {
      super(name, salary, department, id);
      this.reports = [];
      }
      }

      // 实现接口
      interface Printable {
      print(): void;
      }

      class Document implements Printable {
      print() {
      console.log("打印文档");
      }
      }
    • 静态成员与抽象类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      // 静态成员
      class MathUtils {
      static PI: number = 3.14159;

      static calculateCircleArea(radius: number): number {
      return this.PI * radius * radius;
      }
      }

      // 抽象类
      abstract class Shape {
      abstract calculateArea(): number;

      display(): void {
      console.log(`面积是: ${this.calculateArea()}`);
      }
      }

      class Circle extends Shape {
      radius: number;

      constructor(radius: number) {
      super();
      this.radius = radius;
      }

      calculateArea(): number {
      return Math.PI * this.radius * this.radius;
      }
      }
  5. 高级类型

    • 联合类型与交叉类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // 联合类型
      type ID = string | number;

      function printID(id: ID) {
      console.log(`ID: ${id}`);
      }

      // 交叉类型
      type Employee = {
      id: number;
      name: string;
      };

      type Manager = {
      department: string;
      reports: Employee[];
      };

      type ManagerWithEmployeeInfo = Employee & Manager;
    • 类型断言

      1
      2
      3
      4
      5
      6
      7
      // 尖括号语法(不在JSX中使用)
      let someValue: any = "这是一个字符串";
      let strLength: number = (<string>someValue).length;

      // as 语法
      let someValue2: any = "这是另一个字符串";
      let strLength2: number = (someValue2 as string).length;
    • 类型保护

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      // typeof 类型保护
      function process(value: string | number) {
      if (typeof value === "string") {
      return value.toUpperCase(); // 这里 value 被视为 string
      } else {
      return value.toFixed(2); // 这里 value 被视为 number
      }
      }

      // instanceof 类型保护
      class Dog {
      bark() {
      console.log("汪汪!");
      }
      }

      class Cat {
      meow() {
      console.log("喵喵!");
      }
      }

      function makeSound(animal: Dog | Cat) {
      if (animal instanceof Dog) {
      animal.bark(); // 这里 animal 被视为 Dog
      } else {
      animal.meow(); // 这里 animal 被视为 Cat
      }
      }
    • 字面量类型和可辨识联合

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      // 字面量类型
      type Direction = "north" | "south" | "east" | "west";

      // 可辨识联合
      interface Circle {
      kind: "circle";
      radius: number;
      }

      interface Square {
      kind: "square";
      sideLength: number;
      }

      type Shape = Circle | Square;

      function calculateArea(shape: Shape): number {
      switch (shape.kind) {
      case "circle":
      return Math.PI * shape.radius ** 2;
      case "square":
      return shape.sideLength ** 2;
      }
      }
  6. 泛型编程

    • 泛型基础

      1
      2
      3
      4
      5
      6
      7
      8
      // 泛型函数
      function identity<T>(arg: T): T {
      return arg;
      }

      // 使用
      let output1 = identity<string>("myString");
      let output2 = identity(42); // 类型推断为 number
    • 泛型接口与类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      // 泛型接口
      interface GenericBox<T> {
      value: T;
      getValue(): T;
      }

      // 泛型类
      class Box<T> implements GenericBox<T> {
      value: T;

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

      getValue(): T {
      return this.value;
      }
      }
    • 泛型约束

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      // 使用接口约束泛型
      interface HasLength {
      length: number;
      }

      function getLength<T extends HasLength>(arg: T): number {
      return arg.length;
      }

      // 使用
      getLength("string"); // 可以,字符串有length属性
      getLength([1, 2, 3]); // 可以,数组有length属性
      // getLength(123); // 错误,数字没有length属性
    • 泛型工具类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // 内置工具类型
      interface Person {
      name: string;
      age: number;
      }

      // 将所有属性设为可选
      type PartialPerson = Partial<Person>;

      // 将所有属性设为必需
      type RequiredPerson = Required<Person>;

      // 将所有属性设为只读
      type ReadonlyPerson = Readonly<Person>;

      // 提取特定属性
      type NameOnly = Pick<Person, "name">;

      // 排除特定属性
      type PersonWithoutAge = Omit<Person, "age">;
  7. 模块与命名空间

    • ES 模块

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // math.ts
      export function add(x: number, y: number): number {
      return x + y;
      }

      export function subtract(x: number, y: number): number {
      return x - y;
      }

      // app.ts
      import { add, subtract } from "./math";

      console.log(add(5, 3)); // 8
      console.log(subtract(5, 3)); // 2
    • 命名空间

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      // 命名空间(较旧的方式,现代项目推荐使用ES模块)
      namespace Validation {
      export interface StringValidator {
      isValid(s: string): boolean;
      }

      export class RegexValidator implements StringValidator {
      private regex: RegExp;

      constructor(regex: RegExp) {
      this.regex = regex;
      }

      isValid(s: string): boolean {
      return this.regex.test(s);
      }
      }
      }

      // 使用
      let emailValidator = new Validation.RegexValidator(
      /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
      );
      console.log(emailValidator.isValid("test@example.com"));
    • 模块解析策略

      • Node.js/CommonJS 解析
      • Classic 解析
      • 路径映射配置 (tsconfig.json 中的 paths 和 baseUrl)
  8. 声明文件与类型定义

    • .d.ts 文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 全局变量声明
      declare const VERSION: string;

      // 全局函数声明
      declare function fetchData(url: string): Promise<any>;

      // 全局命名空间
      declare namespace MyLib {
      function makeGreeting(name: string): string;
      let version: number;
      }
    • 第三方库类型声明

      1
      2
      # 安装第三方库类型定义
      npm install --save-dev @types/lodash
    • 模块扩展

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 为现有模块添加新功能
      declare module "express" {
      interface Request {
      user?: {
      id: string;
      name: string;
      };
      }
      }
    • 编写声明文件的最佳实践

      • 保持类型定义精确
      • 提供尽可能多的类型信息
      • 使用 readonly 表示不可变属性
      • 考虑类型库的可维护性
  9. TypeScript 编译配置

    • tsconfig.json 文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      {
      "compilerOptions": {
      "target": "es6", // 编译目标 ES 版本
      "module": "commonjs", // 模块系统
      "strict": true, // 启用所有严格类型检查选项
      "esModuleInterop": true, // 启用 ES 模块互操作性
      "skipLibCheck": true, // 跳过声明文件的类型检查
      "forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
      "outDir": "./dist", // 输出目录
      "rootDir": "./src" // 源码目录
      },
      "include": ["src/**/*"], // 包含的文件
      "exclude": ["node_modules"] // 排除的文件
      }
    • 编译选项详解

      • noImplicitAny:不允许隐式的 any 类型
      • strictNullChecks:启用严格的 null 检查
      • noImplicitThis:不允许 this 的类型为 any
      • alwaysStrict:以严格模式解析并为每个源文件生成 “use strict”
      • sourceMap:生成相应的 .map 文件
      • declaration:生成相应的 .d.ts 文件
    • 项目引用

      1
      2
      3
      {
      "references": [{ "path": "../common" }]
      }
    • 构建工具集成

      • webpack 与 ts-loader
      • Babel 与 @babel/preset-typescript
      • esbuild 等现代构建工具
  10. TypeScript 与框架集成

    • React 与 TypeScript

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      // 函数组件
      interface GreetingProps {
      name: string;
      count?: number;
      }

      const Greeting: React.FC<GreetingProps> = ({ name, count = 1 }) => {
      return (
      <h1>
      Hello, {name}! You've visited {count} times.
      </h1>
      );
      };

      // 使用 hooks
      const [state, setState] = React.useState<string>("");
    • Vue 与 TypeScript

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      import { defineComponent, ref } from "vue";

      export default defineComponent({
      props: {
      message: {
      type: String,
      required: true,
      },
      },
      setup(props) {
      const count = ref(0);

      function increment() {
      count.value++;
      }

      return {
      count,
      increment,
      };
      },
      });
    • Svelte 与 TypeScript

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <script lang="ts">
      export let message: string;
      let count: number = 0;

      function increment(): void {
      count += 1;
      }
      </script>

      <h1>{message}</h1>
      <button on:click={increment}>Clicked {count} times</button>
    • Node.js 与 TypeScript

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import * as express from "express";

      const app = express();
      const port = 3000;

      app.get("/", (req: express.Request, res: express.Response) => {
      res.send("Hello from TypeScript!");
      });

      app.listen(port, () => {
      console.log(`Server running at http://localhost:${port}`);
      });
  11. 高级 TypeScript 技巧

    • 条件类型

      1
      2
      3
      4
      type IsString<T> = T extends string ? true : false;

      type A = IsString<string>; // true
      type B = IsString<number>; // false
    • 映射类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      type Properties = "name" | "age" | "address";

      type User = {
      [P in Properties]: string;
      };

      // 相当于
      // type User = {
      // name: string;
      // age: string;
      // address: string;
      // }
    • 递归类型

      1
      2
      3
      4
      // 递归类型定义嵌套数组
      type NestedArray<T> = Array<T | NestedArray<T>>;

      const nested: NestedArray<number> = [1, 2, [3, 4, [5, 6]]];
    • 模板字面量类型

      1
      2
      3
      type EventName<T extends string> = `${T}Changed`;

      type Events = EventName<"name" | "title">; // 'nameChanged' | 'titleChanged'
    • infer 关键字

      1
      2
      3
      4
      5
      6
      7
      type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

      function add(a: number, b: number): number {
      return a + b;
      }

      type AddReturn = ReturnType<typeof add>; // number
  12. TypeScript 与设计模式

    • 单例模式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      class Singleton {
      private static instance: Singleton;

      private constructor() {}

      public static getInstance(): Singleton {
      if (!Singleton.instance) {
      Singleton.instance = new Singleton();
      }
      return Singleton.instance;
      }

      public someMethod(): void {
      console.log("方法已被调用");
      }
      }

      // 使用
      const instance1 = Singleton.getInstance();
      const instance2 = Singleton.getInstance();

      console.log(instance1 === instance2); // true
    • 工厂模式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      interface Product {
      operation(): string;
      }

      class ConcreteProduct1 implements Product {
      operation(): string {
      return "ConcreteProduct1";
      }
      }

      class ConcreteProduct2 implements Product {
      operation(): string {
      return "ConcreteProduct2";
      }
      }

      class Factory {
      createProduct(type: string): Product {
      if (type === "1") {
      return new ConcreteProduct1();
      } else {
      return new ConcreteProduct2();
      }
      }
      }
    • 装饰器模式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      // 需要在 tsconfig.json 中启用 experimentalDecorators
      function log(
      target: any,
      propertyKey: string,
      descriptor: PropertyDescriptor
      ) {
      const originalMethod = descriptor.value;

      descriptor.value = function (...args: any[]) {
      console.log(`Calling ${propertyKey} with:`, args);
      const result = originalMethod.apply(this, args);
      console.log(`Result:`, result);
      return result;
      };

      return descriptor;
      }

      class Calculator {
      @log
      add(a: number, b: number): number {
      return a + b;
      }
      }

      const calc = new Calculator();
      calc.add(1, 2);
      // 输出:
      // Calling add with: [1, 2]
      // Result: 3