TypeScript实战系列之强力爆破泛型的困扰

目录

介绍

泛型在typescript 中使用频率相当高,也给了初学者相当大的阻碍。希望这一篇文章,能帮助你们爆破它。

开始

下面通过模拟实现一个简易版本的axios来引入泛型的使用

typescript 复制代码
// axios.ts
type Method =
  | 'get'
  | 'GET'
  | 'delete'
  | 'Delete'
  | 'head'
  | 'HEAD'
  | 'options'
  | 'OPTIONS'
  | 'post'
  | 'POST'
  | 'put'
  | 'PUT'
  | 'patch'
  | 'PATCH'


interface AxiosRequestConfig {
    baseURL?: string //便于为 axios 实例的方法传递相对 URL
    url?: string // 请求地址 
    /* 注 ? 表示可选 */
    method?: Method // 请求方法 可选
    data?: any // post、patch 等类型请求的数据 可选
    params?: any // post、patch 等类型请求的数据 可选
    headers?: any
    responseType?: XMLHttpRequestResponseType
}

type AxiosResponse= {
    /**
     * 响应的data数据
     */
    data: any
    /**
     * 响应的状态码
     */
    status: number
  }
  

type AxiosPromise = Promise<AxiosResponse>



class Axios {
   default:AxiosRequestConfig={
   }

   constructor(initConfig: AxiosRequestConfig) {
    console.log('axios---init');
   }

   get(url: string, config?: AxiosRequestConfig): AxiosPromise{
    return new Promise((resolve)=>{
        resolve({
            data: {},
            status:200,
        })
    })
   }
}

export const createInstance=(config: AxiosRequestConfig)=>{
   const instance = new Axios(config)
   return instance
}

用它来模拟请求

typescript 复制代码
import {createInstance} from "./axios"

const axios = createInstance({
    baseURL:'https://some-domain.com/api/'
})

axios.get('user/list').then(res=>{
    const {data} = res
})

痛点

这时候 我们使用data 里的数据 是没有代码提示的,因为现在data的类型就是any。

这肯定对于我们开发来说是不方便的!

预期

对于不同的请求,返回的数据格式是不同的。希望每个请求开发者都能自己去定义接口的请求类型!

解决

使用泛型就能完美解决这个痛点!

改造 :

typescript 复制代码
type Method =
  | 'get'
  | 'GET'
  | 'delete'
  | 'Delete'
  | 'head'
  | 'HEAD'
  | 'options'
  | 'OPTIONS'
  | 'post'
  | 'POST'
  | 'put'
  | 'PUT'
  | 'patch'
  | 'PATCH'


interface AxiosRequestConfig<T=unknown> {
    baseURL?: string //便于为 axios 实例的方法传递相对 URL
    url?: string // 请求地址 
    /* 注 ? 表示可选 */
    method?: Method // 请求方法 可选
    data?: T // post、patch 等类型请求的数据 可选
    params?: any // post、patch 等类型请求的数据 可选
    headers?: any
    responseType?: XMLHttpRequestResponseType
}

type AxiosResponse<T=unknown>= {
    /**
     * 响应的data数据
     */
    data: T
    /**
     * 响应的状态码
     */
    status: number
  }
  

type AxiosPromise<T> = Promise<AxiosResponse<T>>



class Axios {
   default:AxiosRequestConfig={
   }

   constructor(initConfig: AxiosRequestConfig) {
    console.log('axios---init');
   }

   get<T=unknown>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>{
    return new Promise((resolve)=>{
        resolve({
            //@ts-ignore
            data: {},
            status:200,
        })
    })
   }
}

export const createInstance=(config: AxiosRequestConfig)=>{
   const instance = new Axios(config)
   return instance
}

使用:

typescript 复制代码
import {createInstance} from "./axios"

const axios = createInstance({
    baseURL:'https://some-domain.com/api/'
})

type UserType = {
    name:string,
    age:number
}

axios.get<UserType>('user/list').then(res=>{
    const {data} = res
})


总结: 泛型是一种用于创建可复用的函数或类的技术,它允许你在编写代码时指定类型参数,以使函数或类适用于不同类型的数据。

如何理解泛型语法

typescript 复制代码
function identity<T>(arg: T): T {
    return arg;
}

看到 语法会感到陌生。它没有什么特别,可以把它类比于函数的入参,你可以随意去定义它的参数名,但是一般ts规范使用 大写的英文字母。

例如:T 就是接受的类型 ,T可以在。只有在外部使用它时 ,T的类型才被确定!

typescript 复制代码
function identity<T>(arg: T): T {
    let temp:T;
    let str  = '123'
    str as T
    return arg;
}

也可以接受多个外部类型

typescript 复制代码
function cross<T,K>(arg1: T,arg2:K): T&K {
    return {
        ...arg1,...arg2
    };
}

泛型约束

想要限制外部传入类型必须带有 length 属性,可以使用关键字 extends

typescript 复制代码
function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);
    return arg;
}


改造后

外部传入的类型 必须带有length 属性 ,否则校验不通过

typescript 复制代码
function loggingIdentity<T extends {length:number}>(arg: T): T {
    console.log(arg.length);
    return arg;
}

loggingIdentity({a:12,length:22})

泛型默认值

泛型也支持给默认值

typescript 复制代码
type arrType<T=string> = {
    list:Array<T>
}

let obj:arrType ={
    list: ['2','3']
}

let obj1:arrType<number> ={
    list: [1,2]
}

练习

实现一个函数 来通过属性值来获得对应的值

typescript 复制代码
function getValueByKey(obj:any,key:string){
  return obj[key]
}

let obj = {
    name:'kd',
    age:35
}

let value = getValueByKey(obj,'name')

但是现在没有代码提示,需要使用泛型,一个代表传入对象的类型,一个代表传入key 的类型

typescript 复制代码
function getValueByKey<T,K>(obj:T,key:K):T[K]{
  return obj[key]
}

因为 ts 不知道 T 和 K 的 关系 所以会报错,但是我们开发知道他们的关系 K 是 属于 T的 key值,于是我们给他们加上 类型限制!

typescript 复制代码
function getValueByKey<T extends {[P:string]:any},K extends keyof T>(obj:T,key:K):T[K]{
  return obj[key]
}

keyof 关键字 后面再介绍, 它的作用是取 类型的 key 值的!

代码提示就出来了! 至于为啥使用的时候可以省略尖括号 是因为类型推断

这时候 T 代表 {name:string,age:number} K 代表 "name" | "age"

当然你也可以自己指定

typescript 复制代码
function getValueByKey<T extends {[P:string]:any},K extends keyof T>(obj:T,key:K):T[K]{
  return obj[key]
}



let obj = {
    name:'kd',
    age:35
}

let value = getValueByKey<typeof obj, keyof typeof obj>(obj,'age')

后续

后面再会出一篇ts的高级篇,里面会涉及到更多的泛型的使用。

相关推荐
阿幸软件杂货间2 分钟前
阿幸课堂随机点名
android·开发语言·javascript
一涯4 分钟前
Cursor操作面板改为垂直
前端
我要让全世界知道我很低调11 分钟前
记一次 Vite 下的白屏优化
前端·css
threelab11 分钟前
three案例 Three.js波纹效果演示
开发语言·javascript·ecmascript
1undefined213 分钟前
element中的Table改造成虚拟列表,并封装成hooks
前端·javascript·vue.js
蓝倾1 小时前
淘宝批量获取商品SKU实战案例
前端·后端·api
comelong1 小时前
Docker容器启动postgres端口映射失败问题
前端
花海如潮淹1 小时前
硬件产品研发管理工具实战指南
前端·python
用户3802258598241 小时前
vue3源码解析:依赖收集
前端·vue.js
用户7579419949701 小时前
基于JavaScript的简易Git
javascript