青训营X豆包MarsCode 技术训练营第四课 | 豆包MarsCode AI 刷题

69 阅读1分钟

TypeScript 类与泛型的使用实践记录

TypeScript 作为 JavaScript 的超集,引入了静态类型检查、接口、类、枚举等特性,极大地提升了代码的可维护性和开发效率。其中,**类(Class)泛型(Generics)**是 TypeScript 中两个非常重要的特性。本文将深入探讨 TypeScript 中泛型的使用方法和场景,以及如何通过类型约束来增强代码的灵活性和安全性。

一、TypeScript 类(Class)基础

在 TypeScript 中,类与 ES6 中的类类似,但 TypeScript 提供了更强大的类型检查和面向对象编程特性。

1.1 基本语法

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

const person = new Person('Alice', 30);
person.greet(); // 输出: Hello, my name is Alice

1.2 继承与多态

TypeScript 支持类的继承,通过 extends 关键字实现。

class Employee extends Person {
  employeeId: number;

  constructor(name: string, age: number, employeeId: number) {
    super(name, age);
    this.employeeId = employeeId;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and my employee ID is ${this.employeeId}`);
  }
}

const employee = new Employee('Bob', 25, 12345);
employee.greet(); // 输出: Hello, my name is Bob and my employee ID is 12345

二、TypeScript 泛型(Generics)基础

泛型允许我们在定义函数、接口或类时不预先指定具体的类型,而是在使用时再指定类型。这种方式提高了代码的复用性和灵活性。

2.1 基本语法

function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42); // 类型为 number
const str = identity<string>('Hello'); // 类型为 string

2.2 泛型接口

interface GenericIdentityFn<T> {
  (arg: T): T;
}

function identityFn<T>(arg: T): T {
  return arg;
}

const myIdentity: GenericIdentityFn<number> = identityFn;

2.3 泛型类

class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

三、泛型的使用场景

3.1 通用数据结构

泛型常用于定义通用的数据结构,如链表、栈、队列等。

class Stack<T> {
  private elements: T[] = [];

  push(element: T) {
    this.elements.push(element);
  }

  pop(): T | undefined {
    return this.elements.pop();
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 输出: 2

const stringStack = new Stack<string>();
stringStack.push('a');
stringStack.push('b');
console.log(stringStack.pop()); // 输出: b

3.2 通用算法

泛型可以用于实现通用的算法,如排序、查找等。

function find<T>(array: T[], predicate: (item: T) => boolean): T | undefined {
  for (const item of array) {
    if (predicate(item)) {
      return item;
    }
  }
  return undefined;
}

const numbers = [1, 2, 3, 4, 5];
const foundNumber = find(numbers, n => n === 3);
console.log(foundNumber); // 输出: 3

const strings = ['a', 'b', 'c'];
const foundString = find(strings, s => s === 'b');
console.log(foundString); // 输出: b

四、类型约束(Type Constraints)

在泛型编程中,有时需要对类型参数进行约束,以确保类型参数具有某些属性或方法。TypeScript 提供了 extends 关键字来实现类型约束。

4.1 基本用法

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

loggingIdentity({ length: 10, value: 'Hello' }); // 正确
loggingIdentity(3); // 错误: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'

4.2 使用 keyof 进行键约束

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

const obj = { a: 1, b: 2, c: 3 };
console.log(getProperty(obj, 'a')); // 输出: 1
console.log(getProperty(obj, 'd')); // 错误: Argument of type '"d"' is not assignable to parameter of type '"a" | "b" | "c"'

4.3 组合类型约束

interface FirstLast {
  first: string;
  last: string;
}

function createFullName<T extends FirstLast>(obj: T): string {
  return `${obj.first} ${obj.last}`;
}

const person = { first: 'John', last: 'Doe', age: 30 };
console.log(createFullName(person)); // 输出: John Doe

const invalidPerson = { first: 'Jane' };
console.log(createFullName(invalidPerson)); // 错误: Property 'last' is missing in type '{ first: string; }' but required in type 'FirstLast'

五、泛型的优势

  1. 类型安全: 泛型在编译时进行类型检查,避免了运行时错误。
  2. 代码复用: 泛型允许编写更通用的代码,减少重复代码。
  3. 灵活性: 泛型提供了高度的灵活性,可以在不牺牲类型安全的前提下实现多种类型操作。
  4. 可维护性: 通过泛型,代码更具可读性和可维护性。

六、常见误区与注意事项

  1. 过度使用泛型: 泛型虽然强大,但过度使用可能导致代码复杂化。应根据实际需求合理使用。
  2. 类型约束不足: 在使用泛型时,确保