《四》大話 Typescript泛型

前言: 本文章爲 TypeScript 系列文章. 旨在利用碎片時間快速入門 Typescript. 或從新溫故 Typescript 查漏補缺.在官方 api 的基礎上, 加上一些平常使用的感想. 若是感興趣的話~ 歡迎關注, 後續持續推出文章.javascript

文章列表:前端

目錄結構:java

  • 爲什麼要用泛型
  • 泛型約束
  • 泛型語法
  • 實際應用
  • 高級類型: 索引類型
  • react 實際場景

爲什麼要用泛型

javascript 做爲一門動態語言, 在實際運行的時候,等變量被賦值才知道該變量的類型. 動態語言給實際的編碼帶來了很大的靈活性.react

function getVal(val) {
    retrurn val;
}
getVal(1) // 返回數字類型
getVal('1') // 返回字符串類型
getVal(['2']) // 返回數組類型
複製代碼

可是一樣的, 在代碼運行期間有可能會發生與類型相關的錯誤, 下降了代碼的可維護性. 那下面咱們用 typescript 來定義變量. 爲了支持3種調用方式, 咱們須要聲明三種類型定義. 有2種方法能夠解決上面這個問題.程序員

  1. 函數重載
function getVal(val: number): number 
function getVal(val: string):string 
function getVal(val: any):any {
    return val;
}
複製代碼
  1. 聯合類型
function getVal(val: string | number | any[]):string | number | any[] {
    return val;
}
複製代碼

做爲一個程序員, 上面2種方法讓咱們感覺重複繁瑣. 這是沒法容忍的. 那怎麼樣可讓該函數又能夠在運行的時候被賦值才肯定該變量的類型, 又有必定的類型約束減小相關的錯誤? 答案就是: 泛型ajax

function getVal<T>(val: T): T {
    return val;
}
複製代碼

T 即表明捕獲函數傳入的參數類型, 而後在函數內部使用 T 即用該參數類型聲明其餘變量. 可是咱們從上面的函數看出, 由於 T 是捕獲參數傳入的參數類型,typescript

這個函數能夠傳入任意參數, 與咱們最開始只支持3個類型的需求不符. 因此下面要引入泛型約束.api

泛型約束

type Params=  string | number | any[];
function getVal<T extends Params>(val: T): T {
    return val;
}
getVal(1);
getVal('2');
getVal(['222']);
getVal<number>('3'); // 跟泛型指定的類型不一致, 報錯
getVal({}); // 不是 Param 類型, 報錯
複製代碼

泛型語法

泛型便可以聲明函數, 也能夠聲明類. 也能夠聲明接口數組

class Person<T>{} // 一個尖括號跟在類名後面
function Person<T> {}  // 一個尖括號跟在函數名後面
interface Person<T> {}  // 一個尖括號跟在接口名後面
複製代碼

有些時候, 一個類或者一個函數裏面, 他不止要用到一個動態類型, 他要用到多個. 可是咱們上面只能捕獲一個, 那直接聲明多個不就行了?bash

function getName<T,U> (name: T, id: U): [T, U] {
    return [name, id]
}
getName('peen', 1);
getName('peen', '222'); // 正常
getName<string, number>('peen', '22'); // 報錯: '22'不是number類型
複製代碼

實際應用

在實際項目中, 每一個項目都須要接口請求, 咱們會封裝一個通用的接口請求, 在這個函數裏面, 處理一些常見的錯誤等等. 爲了讓每一個接口調用都有 typescript 約束, 提醒. 這裏使用泛型是很是合適了.

interface IApiSourceParams {
    GetList: IGetList
}
interface IGetList {
    id: number;
}
export function fetchApi<T extends keyof IApiSourceParams>(action: T, params: IApiSourceParams[T]) {
    return ajax({
        url: action,
        method: 'POST',
        body: params
    })
}
fetchApi('GetList', { id: 2 });
fetchApi('GetList', { id: '33' }); // 報錯, id 應該是 number 類型
複製代碼

這樣子, 咱們就給一個通用的接口請求函數增長了類型約束. 在 IApiSourceParams 中擴展每個接口類型便可.

從上面的例子看到了 T extends keyof IApiSourceParams , 這在上文中沒有看到. 實際這種應用場景特別多.

高級類型: 索引類型

索引類型查詢操做符: keyof , 對於任何類型 T, keyof T的結果爲 T上已知的公共屬性名的聯合. 看着話有點繞, 直接看例子吧

interface Person {
    name: string;
    age: number;
}
let personProps: keyof Person; // 'name' | 'age'
複製代碼

索引訪問操做符 : T[K] . 上面的 keyof 實際就是獲取了對象的鍵值, 看一下上面的實際例子

interface IApiSourceParams {
    GetList: IGetList,
    PostApi: IPostApi
}
interface IGetList {
    id: number;
}
export function fetchApi<T extends keyof IApiSourceParams>(action: T, params: IApiSourceParams[T]) {
    return ajax({
        url: action,
        method: 'POST',
        body: params
    })
}
// IApiSourceParams[T] 獲取的即是接口 IApiSourceParams 對應key值的接口 IGetList. 
複製代碼

react 實際場景

泛型在各大庫中都很是普遍的使用, 下面選react中的類和react hook 實地分析

1、react 類

若是使用過 react , 確定見過下面這種語法聲明 props 和 state

class Test extends Component<IProps, IState> {}
複製代碼

咱們再看看 react class 裏面的 typescript 聲明

class Component<P, S> {
        static contextType?: Context<any>;
        context: any;
        constructor(props: Readonly<P>);
        constructor(props: P, context?: any);
        setState<K extends keyof S>(
            state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
            callback?: () => void
        ): void;
        forceUpdate(callBack?: () => void): void;
        render(): ReactNode;
        readonly props: Readonly<P> & Readonly<{ children?: ReactNode }>;
        state: Readonly<S>;
        refs: {
            [key: string]: ReactInstance
        };
    }
複製代碼

能夠看到react以及提早幫忙作好了一些約束

  1. constructor 的 props 屬性都是隻讀屬性
  2. setState 只能是 K extends keyof , 聲明的 state 裏面的類型.
  3. ……

2、React HOOK

咱們隨便找一個最常使用的 useState 方法.

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
複製代碼

因此咱們在使用的時候, 能夠

const [errorMessage, setError] = useState<string>('');

複製代碼

總結

本篇文章講了泛型解決的場景以及語法. 並介紹了一些實際的應用場景. 最後歡迎關注「前端加加」,認真學前端,作個有專業的技術人...

相關文章
相關標籤/搜索