TypeScript系列:第三篇 - 泛型

上一篇 《TypeScript系列:续篇 - 对象类型(含数组、元数组、函数)》 中有提及对象类型中的"泛型"使用,本篇将详细展开...

TypeScript 引入泛型是为了增强代码的复用性、健壮性。

ts 复制代码
interface Box {
  contents: any;
}

interface Box<Type> {
  contents: Type;
}

泛型允许开发者编写一次代码,适用于多种数据类型,减少冗余。

其在编译时提供类型检查,提前发现错误,提高代码的健壮性。

定义

泛型(Generics)本质上是一种参数化类型。

在定义时不指定具体数据类型 ,用一个或多个占位符(通常用字母「如 TUV」)来表示类型参数;而在使用时指定具体的数据类型

ts 复制代码
function identity<T>(arg: T): T {
  return arg;
}

上述函数返回值的类型参数类型相关;可以接收任何类型的参数,并返回相同类型的值。

<T> 指类型参数,可以将其理解为类型声明需要的变量,在调用时传入具体的参数类型。

  • 调用时提供具体类型
ts 复制代码
identity<string>('ligang')
  • 省略不写,TypeScript 通常可以在泛型调用中推断出预期的类型参数。
ts 复制代码
identity('ligang') 

泛型写法

泛型可以在:函数、接口、类和别名 中定义。

泛型函数

定义一个泛型函数identity,它接受一个类型参数 T,并返回这个类型的值

ts 复制代码
function identity<T>(arg: T): T {
    return arg;
}

let output = identity<string>("ligang"); // 当然,这里的string可省略,Typescript会自行推断

泛型接口

定义了一个泛型接口 Lengthwise<T>,具有 length 属性和 value 属性,其中 length 是一个数字,而 value 是一个类型为T的值。

泛型函数 classArray ,接受一个类型为 Lengthwise<T> 的参数,并返回一个类型为 Lengthwise<T> 的值。

ts 复制代码
interface Lengthwise<T> {
  length: number;
  value: T;
}

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

classArray({ value: 'TypeScript', length: 10 });

泛型类

定义泛型类 Box<T> ,其构造函数接受一个类型参数 T 和一个参数 content,并将这个 content 赋值给类的私有属性 _content

ts 复制代码
class Box<T> {
  private _content: T;

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

  get content(): T {
      return this._content;
  }
}

const box = new Box<number>(123);

🐾 泛型别名

传入一个类型,得到这个类型与 undefinednull 的一个联合类型。

ts 复制代码
type Nullable<T> = T | undefined | null;

let a: Nullable<string> = 'ligang';
let b: Nullable<string> = null;
let c: Nullable<string>;
经典案例

泛型与别名的结合实现表格响应体的类型:

1️⃣ 响应结构体

ts 复制代码
interface Res<T> {
  status: 'success' | 'error';	// 状态码
  data: T;											// 返回数据
}

2️⃣ table 数据格式

ts 复制代码
interface TableData<T> {
  pageNumber: number;	// 页码
  pageSize: number;		// 每页条数
  total: number;			// 总条数
  data: T[];					// 表格数据
}

3️⃣ 结合:响应表格结构体

ts 复制代码
type ResTableData<T> = Res<TableData<T>>;
  • 这里的T 为表格数据类型

4️⃣ 调用示例

ts 复制代码
// 表格数据为 string
const tableData: ResTableData<string> = {
  status: 'success',
  data: {
    pageNumber: 1,
    pageSize: 10,
    total: 100,
    data: ['1', '2'],
  },
};

// 表格数据为 city
interface city {
  code: number;
  name: string;
}
const res: ResTableData<city> = {
  status: 'success',
  data: {
    pageNumber: 1,
    pageSize: 10,
    total: 100,
    data: [
      { code: 10010, name: '北京' },
      { code: 10011, name: '上海' },
    ],
  },
};

常见的泛型表示

类型 说明 示例
Array<T> T 可指定任何类型 let myAry: Array<number> = [1, 2, 3]
Map<K, V> KV 分别是键和值的类型参数,可指定任何类型 let myMap: Map<string, number> = new Map()
Set<T> T 可指定任何类型 let mySet: Set<number> = new Set([1, 2, 3])
Promise<T> T 可指定任何类型 let tableData = (): Promise<ResTableData<city>> => fetch('...').then((res) => res.json())
ts 复制代码
interface Array<Type> {
  length: number;
  pop(): Type | undefined;
  push(...items: Type[]): number;
  ...
}

泛型约束

如果需要限制泛型的使用范围,可以使用泛型约束!

ts 复制代码
<Type extends Constraint>

🌿 示例:提供一个获取对象属性的方法

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

getProp({ name: 'ligang', age: '34' }, 'name'); 	 // 正确
getProp({ name: 'ligang', age: '34' }, 'address'); // 错误,address 对象中不存在

具体报错:类型""address""的参数不能赋给类型""name" | "age""的参数

keyof

keyof 是 TypeScript 中的关键字,用于从一个对象类型中提取所有键(属性名)的联合类型。

ts 复制代码
interface Person {
  name: string;
  age: number;
}
type PersonKeys = keyof Person; // 'name' | 'age'

✊ 综上,解释 getProp() ,只能获取对象既存的属性


除了使用 keyof 方式,也可以自定义约束条件~~

🍃 示例:限制必须有 length 属性

函数的作用:比较两个实现了 Len 接口的值的长度,并返回长度较大的那个值。

ts 复制代码
interface Len {
  length: number;
}
function compareLen<T extends Len, U extends Len>(a: T, b: U) {
  return a.length - b.length ? a : b;
}

compareLen([1, 2, 3], 'ab');

总结

泛型不仅提高了代码的可重用性和灵活性,同时也保证了类型的安全性。掌握好泛型的使用对于提升 TypeScript 编程技能至关重要!

相关推荐
轻口味17 小时前
配置TypeScript:tsconfig.json详解
ubuntu·typescript·json
小林rr2 天前
前端TypeScript学习day03-TS高级类型
前端·学习·typescript
web150850966412 天前
前端TypeScript学习day01-TS介绍与TS部分常用类型
前端·学习·typescript
前端熊猫3 天前
省略内容在句子中间
前端·javascript·typescript
禁止摆烂_才浅3 天前
React全家桶 -【高阶函数/高阶组件/钩子】-【forwardRef、mome、useImperativeHandle、useLayoutEffect】
react.js·typescript
TSFullStack3 天前
TypeScript - 控制结构
typescript
高山我梦口香糖3 天前
[react] 优雅解决typescript动态获取redux仓库的类型问题
前端·react.js·typescript
乐闻x3 天前
如何使用 TypeScript 和 Jest 编写高质量单元测试
javascript·typescript·单元测试·jest
Web阿成3 天前
1.学习TypeScript 类型
javascript·typescript
oumae-kumiko4 天前
【JS/TS鼠标气泡跟随】文本提示 / 操作提示
前端·javascript·typescript