有时,书写某个函数时,会丢失一些类型信息(多个位置的类型应该保持一致)或有关联的信息,大多是由 any 引起。泛型就应运而生。
泛型:是指附属于函数、类、接口、类型别名之上的类型。也不会在编译结果中出现。
说白了就一句话,泛型就是为了:动态的传递信息!

很多时候,TS会智能的根据传递的参数,推导出泛型的具体类型。
如果无法完成推导并且又没有传递具体的类型,默认为空对象类型{},因为它的兼容是最好的(鸭子辨型法),但是现在提示的是unknown

泛型可以设置默认值

在函数中使用泛型

在函数名之后,写上 <泛型名称(一般写T啥的) = 默认值,如果你想写的话>
泛型就相当于是一个类型变量,在定义时无法预知是何种类型。只有在调用函数的时候,才传入具体类型,才能知道是什么类型。
但是只要是在内部(类/接口/函数/函数参数里),则直接使用泛型名称,千万不要再加左右尖括号!!

在类、接口、类型别名中使用泛型

直接在对应名称后写上 <泛型名称(一般写T啥的)>

  1. function take<T = number>(arr: T[], n: number): T[] {
  2. if (n >= arr.length) {
  3. return arr;
  4. } else {
  5. const newArr: T[] = [];
  6. for (let i = 0; i < n; i++) {
  7. newArr.push(arr[i]);
  8. }
  9. return newArr;
  10. }
  11. }
  12. const newArr = take([2, 3, 4, 5], 3);
  13. console.log(newArr);
  14. newArr.forEach(item => {//没有使用泛型前, 这里就丢失了item的类型信息
  15. console.log(item);
  16. });
  17. //回调函数:判断数组中的某一项是否满足条件
  18. // type callback<T> = (n: T, i:number) => boolean;
  19. interface callback<T> {
  20. (n: T, i:number): boolean
  21. }
  22. function filter<T>(arr: T[], callback: callback<T>): T[]{
  23. const newArr: T[] = [];
  24. arr.forEach((item, i) => {
  25. if(callback(item, i)){
  26. newArr.push(item);
  27. }
  28. })
  29. return newArr;
  30. }
  31. const arr = [3,4,6,9,8];
  32. console.log(filter(arr, item => item%2 !== 0));

对于类:

  1. export class ArrayHelper<T>{
  2. //把T写到最外面,则作用域这整个类
  3. constructor(private arr:T[]){
  4. }
  5. take(n: number): T[] {//但是如果在这里给take后面加上泛型,则就按到时传入的类型算
  6. if (n >= this.arr.length) {
  7. return this.arr;
  8. } else {
  9. const newArr: T[] = [];
  10. for (let i = 0; i < n; i++) {
  11. newArr.push(this.arr[i]);
  12. }
  13. return newArr;
  14. }
  15. }
  16. shuffle(){
  17. for(let i = 0, len = this.arr.length;i<len;i++){
  18. for (let i = 0; i < len; i++) {
  19. const targetIndex = this.getRandomNum(0, len);
  20. [this.arr[i], this.arr[targetIndex]] = [this.arr[targetIndex], this.arr[i]];
  21. }
  22. }
  23. return this.arr;
  24. }
  25. private getRandomNum(min: number, max: number) {
  26. const sub = max - min;
  27. return Math.floor(Math.random() * sub + min);
  28. }
  29. }
  30. //另外一个文件
  31. import { ArrayHelper } from "./ArrayHelper";
  32. const helper = new ArrayHelper(['3',{5:3},4,3,2,8]);
  33. console.log(helper.shuffle());
  34. console.log(helper.take(2));

泛型约束

利用 泛型继承 接口/类型别名等,均可
泛型其实很常用!泛型约束(用于限制泛型的范围)也常用。

  1. interface hasNameProperty {
  2. name: string
  3. }
  4. //将某个对象的name属性的每个单词的首字母大小,然后将该对象返回
  5. function nameToUpperCase<T extends hasNameProperty>(obj: T): T {
  6. // obj.name//会报红色, 因为它并不知道T里面是否有name属性
  7. //所以需要对T进行约束,起码是个对象,然后必须要有name属性 ---- >利用 泛型继承
  8. obj.name = obj.name.split(' ').map(s => (s[0].toUpperCase() + s.substr(1))).join(' ');
  9. return obj;
  10. }
  11. const o = {
  12. name: 'wu jingwei',
  13. age: 22,
  14. gender: '男',
  15. }
  16. const nerO = nameToUpperCase(o);
  17. console.log(nerO.name);//Wu Jingwei

多泛型

有可能依赖多个类型,那就多给几个泛型变量呗!

  1. //将两个等长数组进行混合, [1,3,4] ['a', 'b', 'c'] => [1, 'a', 3, 'b', 4, 'c']
  2. function mixinArray<T, P>(arr1: T[], arr2: P[]): (T | P)[] {
  3. if (arr1.length !== arr2.length) {
  4. throw new Error('两个数组长度不等');
  5. }
  6. const res: (T | P)[] = [];
  7. for (let i = 0, len = arr1.length; i < len; i++) {
  8. res.push(arr1[i]);
  9. res.push(arr2[i]);
  10. }
  11. return res;
  12. }
  13. const result = mixinArray([1,3,4], ['a', 'b', 'c']);
  14. console.log(result);

练习:自定义字典类

  1. /**
  2. *
  3. 开发一个字典类 Dictionary, 字典中会保存键值对的数据
  4. 键值对的特点:
  5. * 键key:可以是任何类型,但不允许重复
  6. 值value:可以是任何类型
  7. 每个键唯一对应一个值
  8. 所有的键类型一致,所有的值类型也一致???不对吧..
  9. 字典类中对键值对数据的操作:
  10. 1. 设置某个键对应的值,若不存在则添加一个键值对
  11. 2. 给了键,删除对应的键值对
  12. 3. 循环每一个键值对
  13. 4. 得到当前键值对的数量
  14. 5. 判断某个键是否存在
  15. */
  16. //dictionary.ts
  17. export type Callback<T, P> = (key: T, val: P) => void;
  18. export class Dictionary<K, V>{
  19. private keys: K[] = [];
  20. private values: V[] = [];
  21. // private _size: number = 0;
  22. set(key: K, val: V) {
  23. const i = this.keys.indexOf(key);
  24. if (i < 0) {
  25. this.keys.push(key);
  26. this.values.push(val);
  27. } else {
  28. this.values[i] = val;
  29. }
  30. }
  31. forEach(callback: Callback<K, V>) {
  32. this.keys.forEach((k, i) => {
  33. const v = this.values[i];
  34. callback(k, v);
  35. });
  36. }
  37. has(key: K): boolean {
  38. return this.keys.includes(key);
  39. }
  40. delete(key: K) {
  41. const i = this.keys.indexOf(key);
  42. if (i === -1) {
  43. return;
  44. } else {
  45. this.keys.splice(i, 1);
  46. this.values.splice(i, 1);
  47. }
  48. }
  49. //关于size的访问器
  50. get size(){
  51. return this.keys.length;
  52. }
  53. }
  54. //index.ts
  55. import { Dictionary } from "./dictionary/dictionary";
  56. const dic = new Dictionary<string, number>();
  57. dic.set('234', 12354);
  58. dic.set('123', 24355);
  59. dic.set('nb', 666666);
  60. dic.forEach((k,v)=>{
  61. console.log(`${k}: ${v}`);
  62. });
  63. console.log(dic.has('ddd'));
  64. console.log('准备删除键:123');
  65. dic.delete('123');
  66. console.log(dic);