TypeScript 泛型与类型系统:从入门到精通

TypeScript 泛型与类型系统:从入门到精通

TypeScript 作为 JavaScript 的类型超集,为我们提供了强大的类型系统。今天,就让我们一起探索 TS 中的泛型、type 和 interface,解锁类型安全的新姿势!🚀

前言:为什么需要 TypeScript?

JavaScript 作为一门弱类型语言,开发时非常灵活,但这种灵活性也带来了很多问题:

javascript 复制代码
// JavaScript 弱类型,容易出问题
let num = 1;
num = 'hello'; // 这在 JS 中是合法的,但可能导致后续代码出错
num.toFixed(2); // 运行时才会报错:Uncaught TypeError

TypeScript 的出现正是为了解决这些问题!它为我们带来了类型的约束,让错误在编译阶段就能被发现。

有趣的是,TypeScript 的诞生还有一个背景:微软想让 Java 工程师更容易地编写前端代码。如今,React + TypeScript 已经成为前端开发的标配,掌握 TS 是每个前端开发者的必备技能!

ts与js对比

特性 JavaScript TypeScript
类型检查 运行时(动态) 编译时(静态)✅
错误发现 运行时才暴露 编写时即可发现✅
IDE 智能提示 基础 强大、精准✅
代码可读性 依赖注释和命名 类型即文档✅
大型项目维护 易出错、难重构 更安全、易维护✅
学习成本 稍高(需学类型系统)
构建步骤 需编译(tsc)

第一部分:泛型 - 类型的函数

1.1 什么是泛型?

泛型是类型的函数,T 是它的占位符,接受类型参数并返回新类型。泛型在类型层面引入了参数化机制,核心目标是在编译期间提供类型安全,同时保持代码的复用性。

1.2 解决 any 滥用问题

很多 TS 新手会滥用 any 类型:

typescript 复制代码
let a: any = 1; // any 表示可以是任何类型
a = '12'; // 不能滥用any,这失去了TS的意义

function getFirstElement(arr: any[]): any {
  return arr[0];
}

const numbers = [1, 2, 3, 4];
const firstNum = getFirstElement(numbers);
console.log(firstNum); // any类型
firstNum.toFixed(2); // 运行时可能报错!

const strs = ['1', '2', '3'];
const firstStr = getFirstElement(strs);
console.log(firstStr); // any类型

这种情况下,我们失去了 TypeScript 的类型检查优势。泛型正是解决这个问题的利器!

1.3 泛型的基本使用

typescript 复制代码
// 使用泛型来提高函数的复用性
function getFirstElement2<T>(arr: T[]): T | undefined {
  return arr.length ? arr[0] : undefined;
}

// 显式指定类型参数
const firstNum2 = getFirstElement2<number>(numbers);
firstNum2?.toFixed(2); // 安全的使用方式
console.log(firstNum2); // number类型

// TypeScript 类型推导,自动推断类型
const firstStr2 = getFirstElement2(strs);
console.log(firstStr2); // string类型
  1. 定义泛型

    使用 <T>(T 是约定俗成的名称,也可用其他字母)来声明一个类型变量,代表"某种未知类型"。

    ts 复制代码
    function identity<T>(arg: T): T {
      return arg;
    }
  2. 调用泛型函数

    • 自动推导:TypeScript 会根据传入的值自动推断类型。

      ts 复制代码
      const result = identity("hello"); // T 被推断为 string
    • 手动指定:你也可以显式指定类型。

      ts 复制代码
      const result = identity<number>(42); // T 是 number
  3. 应用于数组、对象等 常用于处理多种类型的数组或结构,保持类型安全。

    ts 复制代码
    function getFirst<T>(arr: T[]): T | undefined {
      return arr[0];
    }
    
    getFirst([1, 2, 3]);     // 返回 number | undefined
    getFirst(["a", "b"]);    // 返回 string | undefined

1.4 实践:用泛型实现链表

让我们用一个更复杂的例子来加深理解 - 实现一个支持泛型的链表数据结构:

typescript 复制代码
// 用泛型去声明一个链表
// 支持泛型的节点,可以接受value类型的传参
class NodeItem<T> {
  value: T;
  next: NodeItem<T> | null = null;
  
  constructor(value: T) {
    this.value = value;
  }
}

class LinkedList<T> {
  head: NodeItem<T> | null = null;
  
  append(value: T): void {
    const newNodeItem = new NodeItem(value);
    
    if (!this.head) {
      this.head = newNodeItem;
      return;
    }
    
    let current = this.head;
    while (current.next) {
      current = current.next;
    }
    
    current.next = newNodeItem;
  }
}

// 使用泛型链表存储数字
const numberList = new LinkedList<number>();
numberList.append(1);
numberList.append(2);
numberList.append(3);
console.log(numberList);

// 使用泛型链表存储自定义类型
interface User {
  id: number;
  name: string;
}

const userList = new LinkedList<User>();
userList.append({
  id: 1,
  name: '张三',
});

这个例子展示了泛型的强大之处:我们可以创建可重用的数据结构,同时保持完整的类型安全!

第二部分:type 与 interface - 类型的艺术

在 TypeScript 中,我们有两种主要方式来定义自定义类型:typeinterface。它们看似相似,实则各有特点。

2.1 基本概念与使用

interface(接口)
  • 作用:定义对象的形状(有哪些属性、方法及其类型)。

  • 语法

    ts 复制代码
    interface Person {
      name: string;
      age: number;
    }
  • 使用

    ts 复制代码
    const user: Person = { name: '张三', age: 25 };

特点:支持扩展 (继承)和合并(同名接口自动合并)。


type(类型别名)
  • 作用:给一个类型起个别名,可以是对象、联合类型、原始类型等。

  • 语法

    ts 复制代码
    type Person = {
      name: string;
      age: number;
    }
  • 使用

    ts 复制代码
    const user: Person = { name: '李四', age: 30 };

特点:更灵活,可定义联合类型元组等复杂类型。

typescript 复制代码
// interface 关键字
interface UserDemo {
  name: string;
  age: number;
}

// type 关键字
type UserType = {
  name: string;
  age: number;
}

// 使用方式相同
const u1: UserDemo = { name: '张三', age: 18 };
const u2: UserType = { name: '李四', age: 19 };

2.2 相同点:都能描述对象和函数

都可以描述一个对象或者函数:

typescript 复制代码
// 描述函数类型
interface AddFn {
  (a: number, b: number): number;
}

type AddType = (a: number, b: number) => number;

// 描述对象类型
interface Person {
  name: string;
  age: number;
}

type PersonType = {
  name: string;
  age: number;
};

都允许扩展:

typescript 复制代码
// interface 扩展
interface Person {
  name: string;
}

interface Employee extends Person {
  job: string;
}

// type 扩展
type PersonType = {
  name: string;
};

type EmployeeType = PersonType & {
  job: string;
};

2.3 不同点:各有所长

1. 继承方式不同
typescript 复制代码
// interface 使用 extends
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// type 使用 & 交集运算符
type AnimalType = {
  name: string;
};

type DogType = AnimalType & {
  breed: string;
};
2. 声明合并能力不同
typescript 复制代码
// interface 可以重复声明,会自动合并
interface Animal {
  name: string;
}

interface Animal {
  age: number;
}

const dog: Animal = { name: '旺财', age: 1 }; // 正确

// type 不能重复声明
type AnimalType = { name: string };
// type AnimalType = { age: number }; // 错误:重复标识符
3. 应用范围不同
typescript 复制代码
// type 可以用于定义基础类型、联合类型、元组等
type ID = number | string; // 联合类型
type Point = [number, number, string]; // 元组类型
type NumberAlias = number; // 类型别名

// interface 只能描述对象结构(函数、类)
// interface ID2 = number | string; // 错误!
4. 简单类型别名
typescript 复制代码
// type 支持简单类型的别名
type NumberOther = number;
let c: NumberOther = 1;

// interface 不能为基本类型创建别名
// interface NumberInterface = number; // 错误!

2.4 如何选择:type 还是 interface?

一般建议:

  • 优先使用 interface,直到需要 type 的特定功能
  • 想定义对象结构?优先用 interface(更直观、可扩展)。
  • 需要联合类型或复杂类型?用 type
  • 大多数场景下,两者可互换,按团队习惯选择即可。

总结

让我们通过一个表格来回顾一下 type 和 interface 的主要区别:

特性 interface type
扩展方式 extends &
声明合并 ✅ 支持 ❌ 不支持
描述基本类型 ❌ 不支持 ✅ 支持
联合类型 ❌ 不支持 ✅ 支持
元组类型 ❌ 不支持 ✅ 支持
函数类型 ✅ 支持 ✅ 支持
类实现 ✅ 支持 ❌ 不支持

TypeScript 的类型系统就像是一座精妙的乐高城堡🏰,泛型、type 和 interface 是其中最重要的积木块。掌握它们,你就能构建出既安全又灵活的代码结构!

希望这篇文章能帮助你更好地理解 TypeScript 的类型系统。如果有任何问题,欢迎在评论区讨论!💬

延伸阅读:

相关推荐
Zuckjet_35 分钟前
开启 3D 之旅 - 你的第一个 WebGL 三角形
前端·javascript·3d·webgl
2401_8638014641 分钟前
探索 12 种 3D 文件格式:综合指南
前端·3d
西阳未落1 小时前
C++基础(21)——内存管理
开发语言·c++·面试
ANYOLY2 小时前
Redis 面试宝典
数据库·redis·面试
珍宝商店2 小时前
前端老旧项目全面性能优化指南与面试攻略
前端·面试·性能优化
bitbitDown2 小时前
四年前端分享给你的高效开发工具库
前端·javascript·vue.js
gnip3 小时前
实现AI对话光标跟随效果
前端·javascript
脑花儿4 小时前
ABAP SMW0下载Excel模板并填充&&剪切板方式粘贴
java·前端·数据库
烛阴5 小时前
【TS 设计模式完全指南】构建你的专属“通知中心”:深入观察者模式
javascript·设计模式·typescript
ShineSpark5 小时前
C++面试11——指针与引用
c++·面试