TS 从 0 到 1 - 泛型

2023-05-17 19:56:24 浏览数 (1)

在 C# 和 Java 中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。

设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数、函数返回值。

泛型是允许同一个函数接受不同类型参数的一种模板。和 any 相比,使用泛型来创建可复用的组件要更好,因为泛型会保留参数类型。

# 泛型语法

代码语言:javascript复制
// <T> 用于传递类型
// 可以链式传递给参数类型和返回类型
function identity <T>(value: T): T {
  return value;
}

<T> 中的 T 称类型变量,是希望传递给 identity 函数的类型占位符,同时它被分配给 value 参数来代替它的类型。

T 代表 Type,在定义泛型时通常用作第一个类型变量名称。实际上 T 可以用任何有效名称代替。

  • K(Key):表示对象中的键类型
  • V(Value):表示对象中的值类型
  • E(Element):表示元素类型

不只能定义一个类型变量,可以引入希望定义的任何数量的类型变量。

代码语言:javascript复制
function identity <T, U>(value: T, message: U): T {
  console.log(message);
  return value;
}

console.log(identity<Number, string>(2022, 'cell'));

除了为类型变量显示设定值之外,更常见的是使编译器自动选择这些类型,从而使代码更简洁。

代码语言:javascript复制
function identity <T, U>(value: T, message: U): T {
  console.log(message);
  return value;
}

console.log(identity(2022, 'cell'));

# 泛型接口

代码语言:javascript复制
interface GenericIdentityFn<T> {
  (arg: T): T;
}

# 泛型类

代码语言:javascript复制
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; };

# 泛型工具类型

# typeof

typeof 用来获取一个变量声明或对象的类型。

代码语言:javascript复制
interface Person {
  name: string;
  age: number;
}

const p: Person = {
  name: 'cell',
  age: 18,
};

type PersonType = typeof p; // Person

function toArray(x: number): Array<number> {
  return [x];
}

type Func = typeof toArray; // (x: number) => number[]

# keyof

keyof 用于获取某类型的所有键,其返回类型是联合类型。

代码语言:javascript复制
interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string | number

在 TypeScript 中支持两种索引签名,数字索引和字符串索引:

代码语言:javascript复制
interface StringArray {
  [index: string]: string; // 字符串索引 keyof StringArray => string | number
}

interface StringArray1 {
  [index: number]: string; // 数字索引 keyof StringArray1 => number
}

为了同时支持两种索引类型,要求数字索引的返回值必须是字符串索引返回值的子类。原因是当使用数值索引时,JavaScript 在执行索引操作时,会先把数值索引先转换为字符串索引。所以,keyof { [x: string]: Person } 的结果会返回 string | number

# in

in 用来遍历枚举类型

代码语言:javascript复制
type Keys = "a" | "b" | "c";

type Obj = {
  [p in Keys]: any;
}; // { a: any, b: any, c: any }

# infer

在条件类型语句中,可以用 infer 声明一个类型变量并进行使用。

代码语言:javascript复制
type ReturnType<T> = T extends (
  ...args: any[]
) => infer R
  ? R
  : any;

# extends

可以使用 extends 添加泛型约束。

代码语言:javascript复制
interface Lengthwise {
  length: number;
}

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

loggingIdentity(1); // Error because number doesn't have a .length property
loggingIdentity({ length: 10, value: 3 }); // OK

# Partial

Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ?

代码语言:javascript复制
type Partial<T> = {
  [P in keyof T]?: T[P];
};

代码语言:javascript复制
interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
  title: "Learn TypeScript",
  description: "study hard",
};

const todo2 = updateTodo(todo1, {
  description: "keep on studying",
});

0 人点赞