# TypeScript泛型编程

# 什么是泛型编程

泛型的定义:

在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。

泛型是一种变量,它的值是类型。

泛型编程的定义:

泛型编程(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。

泛型编程就是对类型的编程。

# 实现String.concat函数

// javascript版
const concat = (t, s) => `${t}${s}`;

const foobar = concat('foo', 'bar'); // 'foobar'

// 泛型版
type Concat<T extends string, S extends string> = `${T}${S}`;

type foobar = Concat<'foo', 'bar'>; // 'foobar'

# 泛型编程中的条件分支

// javascript版本
const exchangeFooBar = (t) => {
  if (t === 'foo') {
    return 'bar';
  } else if (t === 'bar') {
    return 'foo';
  } else {
    return 'other';
  }
}

const result = exchangeFooBar('foo'); // 'bar'

// 泛型版
type ExchangeFooBar<T>
	= T extends 'foo'
		? 'bar'
		: T extends 'bar'
			? 'foo'
			: 'ohter';

type Result = ExchangeFooBar<'foo'>; // 'bar'

# 泛型编程中的循环

// javascript for 循环版
function repeat(repeatChar, count) {
  let result = '';
  for (let i = 0; i < count; ++i) {
    result += repeatChar;
  }
  return result;
}

repeat('a', 5); // 'aaaaa'

// javascript 递归版
function repeat(repeatChar, count, r = '', i = 0) {
  if (i === count) {
    return r;
  }
  return repeat(repeatChar, count, r + repeatChar, i + 1);
}

repeat('a', 5); // 'aaaaa'

// 泛型版循环
// 泛型没有for、while关键字,因此只能使用递归来实现循环

// 生产长度为T的元组
type Tuple<T extends number, U extends void[] = []> = 
  U['length'] extends T ? U : Tuple<T, [void, ...U]>

// 通过元组的方式来实现算数运算
type Add<M extends number, N extends number, R extends any[] = [
  ...Tuple<M>,
  ...Tuple<N>,
]> = R['length'];

type Repeat<
  C extends string,
  Count extends number,
  R extends string = '',
  I extends number = 0
> = I extends Count
	? R
	: Repeat<C, Count, `${R}${C}`, Add<I, 1>>; // TS类型不支持算术运算

type R = Repeat<'a', 5>;

TS类型不支持算术运算

# 泛型编程中的其它操作符 - infer

// 提取 tuple 元素
type Item = [id: number, title: string, price: string];

// 获取 tuple 中的第一个元素
type First<T> = T extends [infer FIRST, ...any] ? FIRST : never;
// 获取 tuple 中的最后一个元素
type Last<T> = T extends [...any, infer LAST] ? LAST : never;

type itemId = First<Item>; // number
type itemPrice = Last<Item>; // string

// 提取一部分
type Slice<T> = T extends [...X: [1, 2, 3], ...Y: infer Other, ...Z: [7, 8, 9]] ? Other : never;
type O = Slice<[1, 2, 3, 4, 5, 6, 7, 8, 9]>; // [4, 5, 6]

// 提取 string 字面量类型
// 提取第一个字符
type FirstChar<T> = T extends `${infer F}${string}` ? F : never;
type A = FirstChar<'ABC'>; // 'A'

// 提取除第一个字符之外的其它字符
type OtherChar<T> = T extends `${string}${infer O}` ? O : never;
type O = OtherChar<'ABC'>; // 'BC'

// 提取以 AB 开头的字符串的剩余部分
type ABXCD<T> = T extends `AB${infer N}CD` ? N : never;
type N = ABXCD<'AB123CD'>; // '123'

// 提取以 AB 或 CD开开头的字符串剩余部分
type ABCD = 'AB' | 'CD';
type StartWithABCD<T> = T extends `${ABCD}${infer N}` ? N : never;
// 将会展开为 type StartWithABCD<T> = T extends `AB${infer N}` | `CD${infer N}` ? N : never;
type M = StartWithABCD<'ABCD'>; // 'CD'

// 提取 interface 类型
// 提取 Foo 类型
type Exchange<T> = T extends { foo: infer F, bar: infer B } ? { foo: B, bar: F } : never;
type A = Exchange<{ foo: string, bar: number }>; // { foo: number, bar: string };

// 提取 key 中的事件名
type ExtractEvent<T> = keyof T extends `on${infer E}` ? E : never;
type EventHandler = { onClick: () => void, onAppear: () => void };
type Events = ExtractEvent<EventHandler>; // 'Click' | 'Appear'

# 泛型编程中的其它操作符

type Len = [1, 2, 3]['length']; // 3
type Merged = [...[1, 2, 3], ...[4, 5, 6]]; // [1, 2, 3, 4, 5, 6]

type Bar = { readonly a: string, b?: number };

type Keys = keyof Bar; // 'a' | 'b'
// 移除 readonly和?
type MutableRequired<T> = { -readonly [P in keyof T]-?: T[P] };
type MutableRequiredBar = MutableRequired<Bar>; // { a: string, b: number }
// 添加 readonly和?
type ReadonlyPartial<T> = { +readonly [P in keyof T]+?: T[P] };
type ReadonlyPartialBar = ReadonlyPartial<Bar>; // { readonly a?: string, readonly b?: number }
// 对象类型合并
type FooBar = { foo: string } & Bar; // { foo: string } & { readonly a: string, b?: number }

泛型编程就是对类型的编程。

变量和函数 -> type和泛型类型

条件分支 -> Extends和?

循环 -> 递归

其它操作符 -> infer等

# 泛型编程在日常工作中的应用

// 封装组件
interface Props<T> {
  list: T[];
  ItemComponent: FC<{ item: T }>;
}

export function ListContainer<T>(props: Props<T>) {
  const { list, ItemComponent } = props;
  return (
    <div>
      {list.map(item => ItemComponent({ item })) }
    </div>
  );
}

// memo
function memo<P extends object>(
  Component: FC<P>,
  propsAreEqual?: (
  	prevProps: Readonly<PropsWithChildren<P>>,
  	nextProps: Readonly<PropsWithChildren<P>>
	) => boolean
): NamedExoticComponent<P>;

# 总结

什么是泛型编程

泛型编程就是对类型的编程

泛型编程的优势

安全性、可重用性、低维护成本

泛型编程在日常工作中的应用

封装通用组件、优化代码、实现易用性高的接口

最近更新时间: 2023/7/24 20:46:22