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 的类型系统。如果有任何问题,欢迎在评论区讨论!💬

延伸阅读:

相关推荐
Mintopia2 分钟前
⚔️ WebAI 推理效率优化:边缘计算 vs 云端部署的技术博弈
前端·javascript·aigc
爱学大树锯1 小时前
【Ruoyi 解密 - 09. 前端探秘2】------ 接口路径及联调实战指南
前端
老华带你飞1 小时前
校园二手书交易|基于SprinBoot+vue的校园二手书交易管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·小程序·毕设·校园二手书交易管理系统
萌程序.1 小时前
创建Vue项目
前端·javascript·vue.js
VT.馒头1 小时前
【力扣】2704. 相等还是不相等
前端·javascript·算法·leetcode·udp
linweidong2 小时前
Vue前端国际化完全教程(企业内部实践教程)
前端·javascript·vue.js·多语言·vue-i18n·动态翻译·vue面经
我在书社写代码2 小时前
基于 Bun.js 和 TypeScript 的轻量级后端 API 应用框架
typescript·bun
lukeLiouu2 小时前
augment不能白嫖了?试试claude code + GLM4.5,十分钟搞定起飞🚀
前端
点正2 小时前
使用 Volta 管理 Node 版本和 chsrc 换源:提升开发效率的完整指南
前端
泉城老铁2 小时前
VUE2实现加载Unity3d
前端·vue.js·unity3d