泛型
介绍
考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
当我们需要写一个传入什么类型就得到什么类型的函数
-
使用
any
类型会导致这个函数可以接收任何类型的arg
参数,这样就丢失了一些信息function one(a: any) : any{ return a; } //每一种类型都写一个方法 function one(a: any) : any{ if(typeof a === 'number') { let ret = (a as number) return ret ; } return a; }
-
使用类型变量
function one<T>(a: T) : T{ return a; } let a1 = one<number>(1) let a2 = one(520) //描述T是什么类型的时候,可以在<number>描述它是一个number类型, //也可以类型推论
泛型类型
泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样:
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <U>(arg: U) => U = identity;
我们还可以使用带有调用签名的对象字面量来定义泛型函数:
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: {<T>(arg: T): T} = identity;
这引导我们去写第一个泛型接口了。 我们把上面例子里的对象字面量拿出来做为一个接口:
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
一个相似的例子,我们可以把泛型参数当作整个接口的一个参数。 这样我们就能清楚的知道使用的具体是哪个泛型类型(比如: Dictionary<string>而不只是Dictionary
)。 这样接口里的其它成员也能知道这个参数的类型了。
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
泛型变量使用
如果我们想同时打印出arg
的长度。 我们很可能会这样做:
function loggingIdentity<T>(arg: T): T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}
如果这么做,编译器会报错说我们使用了arg
的.length
属性,但是没有地方指明arg
具有这个属性。 记住,这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有 .length
属性的。
现在假设我们想操作是T
类型的数组而不直接是T
。由于我们操作的是数组,所以.length
属性是应该存在的。 我们可以像创建其它数组一样创建这个数组:
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
function loggingIdentity<T>(arg: Array<T>): Array<T> {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
泛型约束extends
我们有时候想操作某类型的一组值,并且我们知道这组值具有什么样的属性。 在 loggingIdentity
例子中,我们想访问arg
的length
属性,但是编译器并不能证明每种类型都有length
属性,所以就报错了。
function loggingIdentity<T>(arg: T): T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}
相比于操作any所有类型,我们想要限制函数去处理任意带有.length
属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。
为此,我们定义一个接口来描述约束条件。 创建一个包含 .length
属性的接口,使用这个接口和extends
关键字来实现约束:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
loggingIdentity(3); // Error, 类型“number”的参数不能赋给类型“Lengthwise”的参数。
我们需要传入符合约束类型的值,必须包含必须的属性:
loggingIdentity({length: 10, value: 3});
泛型类
泛型类看上去与泛型接口差不多。 泛型类使用( <>
)括起泛型类型,跟在类名后面。
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; };
console.log(myGenericNumber.add(1,2))
类有两部分:静态部分和实例部分。 泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。
案例
- 函数调用时确定参数类型
const fn = <T, U>(a: T, b?: U): T => {
console.log(a, b);
return a;
}
let a = fn<number, string>(1, '2');
- 泛型接口
interface GenericIdentityFn<T, U> {
(a: T, b?: Array<U>): T;
}
const fn1: GenericIdentityFn<number, string> = (a, b) => {
console.log(a, b);
return 1
}
fn1(1,['2']);
泛型vue3+useHooks+axios
-
//.d.ts import type { AxiosResponse } from 'axios'; export interface AxiosResponse<T = any, D = any> { data: T; status: number; statusText: string; headers: RawAxiosResponseHeaders | AxiosResponseHeaders; config: InternalAxiosRequestConfig<D>; request?: any; } type ApiResponse<T = any> = { success: boolean; data: T; code: string; message: string; }; type HttpResponse<T = any> = AxiosResponse<ApiResponse<T>>; type HttpResponseP<T = any> = Promise<HttpResponse<T>>;
//api static getAttrList(data: any): HttpResponseP<string> { return axios.post('/elementAttr/getElementAllList', data); }
//vue3 const searchFormData = ref({ pageNum: 1, pageSize: 10, elementId: '', attrName: '' }); interface FormDataProp { pageNum: number; pageSize: number; elementId: boolean; attrName: number; } //将FormDataProp作为泛型变量 const { requestFn: getAttrList } = useRequest<FormDataProp, any>(DataOrgSceneController.getAttrList); //searchFormData将会和FormDataProp进行TS检测 getAttrList(searchFormData.value).then((res: any) => {});
interface Opt<T> { initData?: T; message?: string; // 成功默认提示 errMessage?: boolean | string; // 失败默认toast提示 } type RequestsType<T = any> = [T];//此时T作为FormDataProp /** * * ReqProps: 请求参数的类型接口 * ResProps:响应参数data的类型接口 * @param apiFn * ...arg剩余参数是数组的形式,这里是为了方便以后扩展 * @param opt * @returns */ export default function useRequest<ReqProps, ResProps extends any>(apiFn: (...arg: RequestsType<ReqProps>) => HttpResponseP<ResProps>, opt?: Opt<ResProps>) { const options = { errMessage: true, ...opt, }; const loading = ref(false); const data = ref(options.initData as Resp) as Ref<Resp>; const error = ref(null) as Ref<any>; const showError = (message: string, code?: string, exceptionType?: number) => { }; function requestFn(...arg: RequestsType<Requests>): Promise<ApiResponse<Resp>> { loading.value = true; return apiFn(...arg) .then((response) => { return response.data; }) .catch((err: HttpError) => { return Promise.reject(err); }) .finally(() => { loading.value = false; }); } return { loading, data, error, requestFn, }; }