原文: Notes on TypeScript: React and Generics
做者:A. Sharif
譯者:博軒
這些筆記應該有助於更好的理解 TypeScript
,而且在查找如何在特定狀況下使用 TypeScript
會頗有幫助。全部示例都是基於 TypeScript 3.2
完成。react
若是您一直在閱讀「TypeScript 筆記」系列,那麼到目前爲止您將看到泛型的普遍使用。雖然咱們一直在使用泛型,可是咱們並無真正的介紹泛型,以及它有什麼用。在本系列的這一部分中,咱們將首先嚐試更好的理解泛型的概念,而後瞭解如何將泛型和 React 和 TypeScript 結合在一塊兒更好的工做。typescript
編寫軟件時,一方面是咱們但願某些功能能夠重用,而無需爲每種可能的輸入類型編寫特定的功能。咱們如下面這個例子做爲起點:數組
function isDefinedNumber(a: number) : boolean { return a !== null || a !== undefined; } function isDefinedString(a: string) : boolean { return a!== null || a !== undefined; }
咱們不會編寫顯式的函數,使用 string
和 number
做爲輸入,而是編寫一個帶有一下簽名的函數:函數
function isDefined<Type>(a: Type) : boolean { return a!== null || a !== undefined; }
isDefined
指望輸入泛型 Type
。TypeScript 將嘗試推斷參數,並指定一個正確的類型。讓咱們接着看看下一個例子,推斷一下返回的類型:this
function of<Type>(a: Type) : Type[] { return [a]; } const toNumbers = of(1); // const toNumbers: number[] const toStrings = of("Test Of"); // const toString: string[]
在 of
示例中,咱們能夠看到咱們甚至不須要定義類型,由於 TypeScript 能夠推斷出參數類型。這並不是適用於全部狀況,有時候,咱們必須明確說明類型。咱們也能夠這樣定義上面的函數:翻譯
const toNumbers = of<number>(1); // const toNumbers: number[] const toStrings = of<string>("Test Of"); // const toString: string[]
從技術上講,咱們可使用 any
:code
function of(a: any) : any { if (a.length !== undefined) { return a } return a; }
可是使用any
和泛型之間會存在很大的差別。若是你仔細看看上面的例子,咱們對輸入的參數一無所知。of
使用 undefined
或者 null
值調用將致使錯誤。泛型能夠推斷出確切的類型,並強制在函數體內相應地處理輸入。下面是使用泛型的相同示例:繼承
function of<Type>(a: Type) : Type[] { if (a.length !== undefined) { // error: Property 'length' does not exist on 'Type' return a } return [a]; }
在處理泛型時咱們必須更加明確,能夠將示例重寫爲如下內容:ip
function of<Type>(a: Type | Type[]) : Type[] { if (Array.isArray(a)) { return a } return [a]; } const a = of(1); // const a: number[] const b = of([1]); // const b: number[]
泛型也能夠應用於重用功能,如參數 a
能夠對應類型 Type
或類型數組 Type
。當 1
做爲參數傳入時,泛型Type
會綁定到 number
類型 ,當傳入 [1]
時會發生一樣的狀況,Type
會被綁定到 number
。get
到這裏咱們已經看到如何在函數中使用泛型,但同時,咱們也能夠將泛型與類一塊兒使用,這使得當你在 React 中編寫類組件可能會很是有趣。
class GenericClass<Type> { of = (a: Type | Type[]): Type[] => { if (Array.isArray(a)) { return a; } return [a]; }; } const genericClass = new GenericClass<number>(); const a = genericClass.of(1); // const a: number[] const b = genericClass.of("1"); // error! const c = genericClass.of([1]); // const c: number[]
到目前爲止咱們看到的示例應該有助於咱們理解基礎知識,咱們將基於這些知識,將泛型與React組件一塊兒使用。
使用 React 時,咱們可能有一個函數組件,咱們須要推斷參數類型。這個組件須要一個 number
或者 string
類型的參數,或者一個number
或者 string
數組類型的參數。
type RowProps<Type> = { input: Type | Type[]; }; function Rows<Type>({input}: RowProps<Type>) { if (Array.isArray(input)) { return <div>{input.map((i, idx) => <div key={idx}>{i}</div>)}</div> } return <div>{input}</div> } // usage <Rows input={[1]} /> <Rows input={1} /> <Rows input={true} /> // Also works!
這樣沒問題,可是它限制依然適用於任何值。咱們能夠傳入 true , TypeScript 不會抱怨。咱們須要嚴格限制 Type
,或者讓 Type
繼承 string
或者 number
類型。
function Rows<Type extends number | string>({input}: RowProps<Type>) { if (Array.isArray(input)) { return <div>{input.map((i, idx) => <div key={idx}>{i}</div>)}</div> } return <div>{input}</div> } <Rows input={[1]} /> <Rows input={1} /> <Rows input="1" /> <Rows input={["1"]} /> <Rows input={true} /> //Error!
咱們能夠確保如今只能傳入預期類型的參數。值得注意的是,咱們也能夠爲 Props
定義通用類型,如上例所示:
type RowProps<Type> = { input: Type | Type[]; };
接下來,咱們將構建一個更高級的示例,以瞭解爲何泛型能夠幫助咱們構建可重用的React組件。咱們將構建一個指望兩個不一樣輸入的組件。基於這些輸入,咱們將計算第三個值,並根據原始輸入計算出新值,爲 render
函數提供 props
。
type RenderPropType<InputType, OtherInputType> = { c: number } & InputType & OtherInputType; type RowComponentPropTypes<InputType, OtherInputType> = { input: InputType; otherInput: OtherInputType; render: (props: RenderPropType<InputType, OtherInputType>) => JSX.Element; };
第一步是定義 RowComponentPropTypes
,咱們用TypeScript推斷參數的類型,並將 RenderPropType
與 render
函數的 props
進行綁定 。RenderPropType
融合了新類型 {c: number}
產生的交集,咱們將一塊兒計算,InputType
和 OtherInputType
。到目前爲止,咱們一直在大量使用泛型。
咱們可能不知道輸入的確切類型,所以咱們的下一步是在組件限制輸入的類型。
class RowComponent< InputType extends { a: number }, OtherInputType extends { b: number } > extends React.Component<RowComponentPropTypes<InputType, OtherInputType>> { // implementation... }
經過使用 InputType extends { a: number }
咱們確保咱們的輸入具備提供number
類型的a
屬性。這個規則一樣適用於 OtherInputType
。如今,咱們實現了 RowComponent
並確保咱們爲 render
函數提供了 a
,b
,c
三個屬性。
最後,這是咱們完整的示例:
class RowComponent< InputType extends { a: number }, OtherInputType extends { b: number } > extends React.Component<RowComponentPropTypes<InputType, OtherInputType>> { convert = (input: InputType, output: OtherInputType) => { return { c: input.a + output.b, ...input, ...output }; }; render() { return this.props.render( this.convert(this.props.input, this.props.otherInput) ); } } <RowComponent input={{ a: 1 }} otherInput={{ b: 2 }} render={({ a, b, c }) => ( <div> {a} {b} {c} </div> )} />
如今,咱們對泛型,以及如何將 React
與 TypeScript
配合使用,應該有個大體的瞭解了。
本文已經聯繫原文做者,並受權翻譯,轉載請保留原文連接