泛型就是在不知道什么类型的时候,用一个变量来暂时替代,以保证后续的类型的一致性

使用泛型

使用方法是在类的声明的时候,用来声明泛型T,后续直接用T来指代这个类型的变量,类、函数、接口都可以使用泛型

  1. class Queue<T> {
  2. queue: T[] = [];
  3. constructor(arr: T[]) {
  4. this.queue = arr;
  5. }
  6. push(item: T) {
  7. this.queue.push(item);
  8. }
  9. shift() {
  10. this.queue.shift();
  11. }
  12. }
  13. let queue = new Queue(["1", "2", "3"]);
  14. console.log(queue);//Queue { queue: [ '1', '2', '3' ] }
  15. queue.push("4");
  16. console.log(queue);//Queue { queue: [ '1', '2', '3', '4' ] }
  17. queue.push(5);//错误
  1. //接口
  2. interface userAction<T> {
  3. canWalk: T;
  4. walk(param: T): T;
  5. }
  6. let a: userAction<string> = {
  7. canWalk: "true",
  8. walk(param) {
  9. return param;
  10. }
  11. };
  12. console.log(a.walk("walking"));

泛型组成的类型

  1. function addOne<T>(arr: T[], item: T): Array<T> {
  2. let ARR = arr;
  3. ARR.push(item);
  4. return ARR;
  5. }
  6. console.log(addOne<number>([1, 2, 3], 1));
  7. console.log(addOne(["1", " 2", " 3"], " 1"));
  8. console.log(addOne<number>([1, 2, 3], '1'));//报错
  9. console.log(addOne<number>(["1", " 2", " 3"], " 1"));//报错

使用了泛型后加不加类型都可以,不加的话就可以写任何类型,保证类型一致即可,但是加上的话就只能写限定的类型了

泛型的默认值

泛型可以像函数参数一样用等号设定默认值,在没有设置具体类型以及无法推断类型的时候会起作用

  1. function createArray<T = string>(length: number, value: T): Array<T> {
  2. let result: T[] = [];
  3. for (let i = 0; i < length; i++) {
  4. result[i] = value;
  5. }
  6. return result;
  7. }

泛型的继承

泛型可以有多个参数,而且泛型也可以有继承关系

  1. function copyFields<T extends U, U>(target: T, source: U): T {
  2. for (let id in source) {
  3. target[id] = (<T>source)[id];
  4. }
  5. return target;
  6. }
  7. let x = { a: 1, b: 2, c: 3, d: 4 };
  8. copyFields(x, { b: 10, d: 20 });

泛型约束

有时候我们需要输出一个变量的length,比如数组、string以及我们自定义的一个带有length属性的变量,这时我们就需要用到泛型约束了

  1. interface hasLength {
  2. length: number;
  3. }
  4. function logLength<T extends hasLength>(param: T) {
  5. console.log(param.length);
  6. }
  7. logLength('111111')
  8. logLength([1,2,3,4])
  9. logLength({length:10})

取对象的值

另外我们有时候需要使用某个对象中的一些属性,但是我们我们不能保证这个属性在对象上,这个时候可以使用泛型约束以及keyof操作符

keyof操作符用来获取键的种类,有自定义的时候就会返回具体的键,没有的时候就会返回键的类型

  1. interface Person {
  2. name: string;
  3. age: number;
  4. location: string;
  5. }
  6. type K1 = keyof Person; // "name" | "age" | "location"
  7. type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
  8. type K3 = keyof { [x: string]: Person }; // string | number

需要注意,在有自定义的键的时候会返回键名的字面量类型组成的联合类型,如下

  1. interface User {
  2. name: string;
  3. age: number;
  4. }
  5. type keys = keyof User//'name'|'age'
  6. let a: keys = "name";
  7. let b: keys = "names";//错误,因为只能赋值'name'或者'age'
  1. function logValue<T, U>(param: T, key: U): void {
  2. console.log(param[key]);
  3. }
  4. //报错Type 'U' cannot be used to index type 'T'
  5. function logValue<T, U extends keyof T>(param: T, key: U): void {
  6. console.log(param[key]);
  7. }
  8. //通过

因为keyof得到了对象的键,因此要取得对象的键值就不会报错。