TypeScript 类、泛型的使用实践记录

44 阅读3分钟

使用过ts的朋友都知道,ts有很多优点,它提供静态类型检查,能帮我们规避很多类型相关的错误,类型的定义,也帮助其他开发者理解我们的代码等等,但是有时候我想让某个函数返回什么值,或者我也不知道我实现某个功能需要哪些形参,并且定义某个类型后,使用时就有了很多约束,减少了代码的复用性,这个时候可以用到我们的泛型。

介绍

泛型是TypeScript中一个非常强大的特性,它允许我们在定义函数、接口或类的时候不预先指定具体的类型,而是在使用的时候再指定类型,从而增加代码的复用性和类型安全性。

泛型的使用方法

  1. 泛型函数

    泛型函数允许在函数定义时不指定参数和返回值的类型,而是在调用时指定。

    
    	function identity<T>(arg: T): T {
    
    	    return arg;
    
    	}
    
    	 
    
    	let output = identity<string>("myString");  // 类型参数为string
    
    	let numOutput = identity<number>(123);      // 类型参数为number
    
  2. 泛型接口

    泛型接口允许在接口定义时不指定属性的类型,而是在实现接口时指定。

    
    	interface GenericIdentityFn<T> {
    
    	    (arg: T): T;
    
    	}
    
    	 
    
    	function identity<T>(arg: T): T {
    
    	    return arg;
    
    	}
    
    	 
    
    	let myIdentity: GenericIdentityFn<number> = identity;
    
  3. 泛型类

    泛型类允许在类定义时不指定属性的类型,而是在实例化类时指定。

    
    	class GenericNumber<T> {
    
    	    zeroValue: T;
    
    	    add: (x: T, y: T) => T;
    
    	 
    
    	    constructor(zeroValue: T, add: (x: T, y: T) => T) {
    
    	        this.zeroValue = zeroValue;
    
    	        this.add = add;
    
    	    }
    
    	}
    
    	 
    
    	let myGenericNumber = new GenericNumber<number>(0, function(x, y) { return x + y; });
    
    	console.log(myGenericNumber.add(1, 2)); // 输出 3
    

使用类型约束增加代码的灵活性和安全性

虽然泛型提供了很大的灵活性,但有时候我们也需要对泛型进行一定的约束,以保证类型安全,如果全使用泛型似乎就丢弃了ts的一个特点。这时可以使用类型约束(Type Constraints)。

  1. 类型约束的基本语法

    类型约束通过在泛型后面使用extends关键字来指定一个约束条件。

    
    	interface Lengthwise {
    
    	    length: number;
    
    	}
    
    	 
    
    	function loggingIdentity<T extends Lengthwise>(arg: T): T {
    
    	    console.log(arg.length);  // 现在我们可以访问arg.length属性了
    
    	    return arg;
    
    	}
    
    	 
    
    	loggingIdentity({ length: 10, value: 3 }); // 正确
    
    	loggingIdentity({ value: 3 }); // 错误,因为缺少length属性
    
  2. 在泛型类中使用类型约束

    
    	class GenericArray<T extends { length: number }> {
    
    	    private _array: T[];
    
    	 
    
    	    constructor(array: T[]) {
    
    	        this._array = array;
    
    	    }
    
    	 
    
    	    push(item: T): number {
    
    	        return this._array.push(item);
    
    	    }
    
    	 
    
    	    get length(): number {
    
    	        return this._array.length;
    
    	    }
    
    	 
    
    	    logLengths(): void {
    
    	        this._array.forEach(item => {
    
    	            console.log(item.length);
    
    	        });
    
    	    }
    
    	}
    
    	 
    
    	let strings = new GenericArray([{ length: 5, value: "hello" }, { length: 3, value: "world" }]);
    
    	console.log(strings.length); // 输出 2
    
    	strings.logLengths(); // 输出 5 和 3
    

泛型的使用场景

  1. 函数参数和返回值的类型一致

    当函数的参数和返回值的类型需要保持一致时,可以使用泛型。

  2. 组件库的开发

    在开发组件库时,为了增加组件的复用性和类型安全性,可以使用泛型来定义组件的属性和事件。

  3. 数据结构的定义

    在定义数据结构时,如链表、栈、队列等,可以使用泛型来定义节点的类型,从而增加数据结构的通用性。

  4. 类型安全的API请求

    在编写与后端API交互的代码时,可以使用泛型来定义请求和响应的类型,从而增加代码的类型安全性。

以上使用场景为小编的了解到的,具体需要根据项目的需求进行选择