(注明:目前泛型理解不够清晰,先照葫芦画瓢,整理一个粗略版本,后续会再次补充)

一、泛型的概念

​ 在TS中,泛型(Generics)是一种创建可复用代码的方式,类似于代码组件的概念。具体来说,就是在定义接口、函数或类的时候,不预先指定参数、返回值的类型,而是在使用时,根据具体情况来指定相应的类型。

​ 举个例子,定义一个函数 identity ,该函数有一个参数,函数内部就是实现了将参数原样返回,那么代码如下:

const identity = (arg) => arg;

​ 然后我们给代码加上类型声明,并使函数的入参和返回值都应该可以是任意类型:

type idBoolean = (arg: boolean) => boolean;
type idNumber = (arg: number) => number;
type idString = (arg: string) => string;
...

​ 上面的实现办法就是,TS有多少种类型就,写多少行代码,对应不同的类型签名,但这样会使代码冗余,难以维护。还有一种办法是使用any类型,不过这样会丧失类型检查功能,得不偿失:

const identity = (arg: any) => any;// 哪怕返回值并没有这个方法,但依旧不会报错
identity("string").length; // ok
identity("string").toFixed(2); // ok
identity(null).toString(); // ok

​ 如果我们想要实现:根据传递的值得类型进行推导,并根据推导出来的类型,进行类型检查,比如我传了一个string,但是返回值使用量number的方法,那么就应该报错,这种效果,就需要借助泛型来实现:

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

​ 上面代码中的T,表示Type,是一个抽象类型,只有在调用函数时,才会确认具体的类型,这样就能适用于不同类型的数据。调用函数时,先把类型传递给 <T>中的T,然后再链式传递给参数类型和返回值类型。

​ 在泛型的<>中,我们可以定义任何数量的类型变量:

function identity <T, U>(value: T, message: U) : T {  console.log(message);  return value;
}
// 调用时,使用 <> 定义好对应类型变量的类型 像传参一样 一一对应
console.log(identity<Number, string>(68, "Semlinker"));

​ 在调用函数时,也可以省略使用 <> 显式设定类型,编译器可以自动根据我们的参数类型来得知对应类型:

function identity <T, U>(value: T, message: U) : T { console.log(message);  return value;
}
// 省略 <>
console.log(identity(68, "Semlinker"));

二、泛型约束

​ 如果我们不对泛型的类型变量进行约束,那么其类型理论上是可以是任何类型,那这样只能使用所有类型共有的属性或方法,否则就会报错:

function trace<T>(arg: T): T { console.log(arg.size); // 报错 Error: Property 'size doesn't exist on type 'T' return arg;
}

​ 所以我们要对其进行约束,方式就是通过 extends 继承一个接口:

interface Sizeable {size: number;
}
function trace<T extends Sizeable>(arg: T): T {console.log(arg.size);  return arg;
}

三、泛型工具类

1、概念

​ 为了方便开发,TS内置了一些常用的工具类型,比如 Partial、Required等。不过在学习这些内置工具类型之前,我们需要先去了解一些基础的TS的特性。

2、基础特性

① typeof

​ typeof 在 JS 中是用来判断值的类型,在 TS 中,typeof 不仅能判断基本数据类型,还可以判断接口类型:

interface Person { name: string; age: number;
}
const sem: Person = { name: "semlinker", age: 30 };
console.log(typeof sem) // Person
// 获取接口类型 并赋值给 类型变量
type Sem = typeof sem; // type Sem = Person
const lolo: Sem = { name: "lolo", age: 5 }

​ 以及获取对象的属性类型结构,作为一个类型赋值给类型变量:

const Message = {  name: "jimmy",   age: 18,   address: {   province: '四川',   city: '成都'     }
}
type message = typeof Message;
/*type message = {  name: string;  age: number;  address: {     province: string;      city: string;  };
}
*/// 函数对象也可以
function toArray(x: number): Array<number> {return [x];
}
type Func = typeof toArray; // type Func = (x: number) => number[]
② keyof

​ keyof 操作符 是 TS 2.1 版本引入的,用来获取某种类型的所有键,其返回类型是一个联合类型:

interface Person { name: string; age: number;
}// 获取接口类型的键
type K1 = keyof Person; // "name" | "age"
// 支持获取数组类型的键
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
// 支持普通数据类型的键
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"
③ in

​ in 用来遍历枚举类型,比如联合类型:

type Keys = "a" | "b" | "c"type Obj =  { [p in Keys]: any
} // -> { a: any, b: any, c: any }
④ infer(不清晰)

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

type ReturnType<T> = T extends ( ...args: any[]
) => infer R ? R : any;

​ 以上代码中 infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。

⑤ extends

​ extends 主要用于给泛型添加约束:

interface Lengthwise { length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg;
}
⑥ 映射类型

​ 根据已经存在的类型创建出新的类型,那被创建的新类型,我们就称为映射类型:

// 已存在的接口类型
interface TestInterface{  name:string,   age:number
}
// 处理已存在类型 并生成新类型的方法
type OptionalTestInterface<T> = { [p in keyof T]+?:T[p]
}
// 获取新类型
type newTestInterface = OptionalTestInterface<TestInterface>

3、内置工具类型

① Partial

Partial<T> 结合keyof 和 in ,将某类型的所有属性变成可选:

// 工具类型 Partial 内部原理
type Partial<T> = { [P in keyof T]?: T[P];
};
// 已存在的接口类型
interface UserInfo {  id: string;  name: string;
}
// 使用Partial
type NewUserInfo = Partial<UserInfo>;
const xiaoming: NewUserInfo = {  name: 'xiaoming'
}

​ 但 Partial 也有它的局限性:只支持对第一层属性进行处理,如果要处理的类型中有嵌套属性,则第二层往下就不会再处理。但如果想要对嵌套属性也进行处理,可以自己实现:

type DeepPartial<T> = {   // 如果是 object,则递归类型  [U in keyof T]?: T[U] extends object    ? DeepPartial<T[U]>   : T[U]
};type PartialedWindow = DeepPartial<T>; // 现在T上所有属性都变成了可选啦
② Required

​ Required将某类型的所有属性变成必选:

// Required 内部原理
type Required<T> = {    [P in keyof T]-?: T[P] // 其中 -? 是代表移除 ? 这个 modifier 的标识
};
③ Readonly

Readonly<T> 的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值:

//  Readonly 内部原理
type Readonly<T> = { readonly [P in keyof T]: T[P];
};
// 使用
interface Todo {title: string;
}
const todo: Readonly<Todo> = { title: "Delete inactive users"
};
todo.title = "Hello"; // Error: cannot reassign a readonly property
④ Pick

​ Pick 从某个类型中挑出部分属性出来:

// 原理
type Pick<T, K extends keyof T> = {  [P in K]: T[P];
};
// 使用
interface Todo { title: string; description: string;completed: boolean;
}type TodoPreview = Pick<Todo, "title" | "completed">;const todo: TodoPreview = { title: "Clean room", completed: false,
};
⑤ ReturnType

​ ReturnType 用来得到一个函数的返回值类型:

// 原理
type ReturnType<T extends (...args: any[]) => any> = T extends ( ...args: any[]
) => infer R   // infer在这里用于提取函数类型的返回值类型? R : any;
// 使用
type Func = (value: number) => string;
// ReturnType获取到 Func 的返回值类型为 string,所以,foo 也就只能被赋值为字符串了。
const foo: ReturnType<Func> = "1";
⑥ Exclude

Exclude<T, U> 的作用是将某个类型中属于另一个的类型移除掉,简单来说就是取不同的部分:

// 原理
type Exclude<T, U> = T extends U ? never : T;
// 使用
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
⑦ Extract

Extract<T, U> 的作用是从 T 中提取出 U,简单来说就是取相同的部分:

// 原理
type Extract<T, U> = T extends U ? T : never;
// 使用
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract<string | number | (() => void), Function>; // () =>void
⑧ NonNullable

NonNullable<T> 的作用是用来过滤类型中的 nullundefined 类型:

// 原理
type NonNullable<T> = T extendsnull | undefined ? never : T;
// 使用
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]
⑨ Record、Omit、Parameters等

TypeScript 学习笔记(四)--- 泛型(Generics)相关推荐

  1. Typescript 学习笔记七:泛型

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  2. Typescript 学习笔记一:介绍、安装、编译

    前言 整理了一下 Typescript 的学习笔记,方便后期遗忘某个知识点的时候,快速回忆. 为了避免凌乱,用 gitbook 结合 marketdown 整理的. github地址是:ts-gitb ...

  3. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  4. IOS学习笔记(四)之UITextField和UITextView控件学习

    IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...

  5. RabbitMQ学习笔记四:RabbitMQ命令(附疑难问题解决)

    RabbitMQ学习笔记四:RabbitMQ命令(附疑难问题解决) 参考文章: (1)RabbitMQ学习笔记四:RabbitMQ命令(附疑难问题解决) (2)https://www.cnblogs. ...

  6. JSP学习笔记(四十九):抛弃POI,使用iText生成Word文档

    POI操作excel的确很优秀,操作word的功能却不敢令人恭维.我们可以利用iText生成rtf文档,扩展名使用doc即可. 使用iText生成rtf,除了iText的包外,还需要额外的一个支持rt ...

  7. Ethernet/IP 学习笔记四

    Ethernet/IP 学习笔记四 EtherNet/IP Quick Start for Vendors Handbook (PUB213R0): https://www.odva.org/Port ...

  8. TypeScript学习笔记3:运算符

    TS 和 JS 相对比的优势 TypeScript的安装步骤.运行问题及代码的简单运行 TypeScript学习笔记1:变量赋值及书写方式 TypeScript学习笔记2:数据类型 文章目录 运算符 ...

  9. TypeScript学习笔记2:数据类型

    TS 和 JS 相对比的优势 TypeScript的安装步骤.运行问题及代码的简单运行 TypeScript学习笔记1:变量赋值及书写方式 TypeScript学习笔记2:数据类型 文章目录 数据类型 ...

  10. TypeScript学习笔记1:变量赋值及书写方式

    TS 和 JS 相对比的优势 TypeScript的安装步骤.运行问题及代码的简单运行 TypeScript学习笔记1:变量赋值及书写方式 TypeScript学习笔记2:数据类型 文章目录 变量赋值 ...

最新文章

  1. 用composer安装laravel-bjyblog
  2. Modelsim下进行功能仿真没问题,可是在ISE综合报错,如何解决?
  3. 【Netty】使用 Netty 开发 HTTP 服务器 ( HTTP 请求过滤 )
  4. python 分类变量回归_极简Python带你探索分类与回归的奥秘
  5. WPF之鼠标滑动切换图片
  6. /usr/bin/python^M: bad interpreter: No such file or directory
  7. 腾讯朋友、钉钉等被微信违规公示点名;谷歌更新安卓修复数百万台芯片漏洞;微软终止支持.NET Core 3.0 | 极客头条...
  8. 项目中js文件修改后浏览器不能及时更新的解决办法
  9. 数据结构之算法与线性表
  10. lc滤波器是利用电感的感抗_电感器在电路中的应用特性
  11. ROI和widthStep
  12. python数据结构之动态规划
  13. VScode环境变量配置
  14. 管道读写报错121:信号灯超时时间已到
  15. Gitee+PicGo+Typora图床搭建丨出现 Error: You must be logged in to use 的解决方法
  16. 入门pandas—数据透视与交叉表
  17. 02C++对C的增强
  18. 信息年龄、新鲜度、数据寿命、边缘计算等读书报告
  19. 操作系统之哲学原理 第2版
  20. 视频号引流有哪些方法?学会这几种方法快速吸粉

热门文章

  1. 语句摘抄——第22周
  2. 领英封号怎么办,如何避免封号?
  3. 公网访问阿里云RDS云数据库
  4. SQL 基础(五)数据查询实战演练一
  5. java dms_奥点云-DMS Java API 文档
  6. 理科生学计算机数字媒体方向,文科生,理科生,分别可以报哪些专业?5分钟了解文理报考大方向...
  7. 239页11万字新型智慧城市运营中心IOC大数据平台建设方案
  8. 深度学习图像数据库总结(收藏用)
  9. elasticsearch进阶(3)—— ilm policy
  10. java咖啡是研磨的吗_咖啡的研磨程度,真的很重要吗?