<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【TS】1109- React + TypeScript 實踐經(jīng)驗總結(jié)

          共 16428字,需瀏覽 33分鐘

           ·

          2021-10-17 23:36

          ?? 準(zhǔn)備知識

          • 熟悉 React
          • 熟悉 TypeScript (參考書籍:2ality's guide[1], 初學(xué)者建議閱讀:chibicode's tutorial[2]

          • 熟讀 React 官方文檔 TS 部分[3]

          • 熟讀 TypeScript playground React 部分[4]

          本文檔參考 TypeScript 最新版本

          如何引入 React

          import?*?as?React?from?'react'

          import?*?as?ReactDOM?from?'react-dom'

          這種引用方式被證明[5]是最可靠的一種方式, 推薦使用

          而另外一種引用方式:

          import?React?from?'react'

          import?ReactDOM?from?'react-dom'

          需要添加額外的配置:"allowSyntheticDefaultImports": true

          函數(shù)式組件的聲明方式

          聲明的幾種方式

          第一種:也是比較推薦的一種,使用 React.FunctionComponent,簡寫形式:React.FC:

          //?Great
          type?AppProps?=?{
          ??message:?string
          }

          const?App:?React.FC?=?({?message,?children?})?=>?(
          ??<div>
          ????{message}
          ????{children}
          ??div>

          )

          使用用 React.FC 聲明函數(shù)組件和普通聲明以及 PropsWithChildren 的區(qū)別是:

          • React.FC 顯式地定義了返回類型,其他方式是隱式推導(dǎo)的
          • React.FC 對靜態(tài)屬性:displayName、propTypes、defaultProps 提供了類型檢查和自動補全

          • React.FC 為 children 提供了隱式的類型(ReactElement | null),但是目前,提供的類型存在一些 issue[6](問題)

          比如以下用法 React.FC 會報類型錯誤:

          const?App:?React.FC?=?props?=>?props.children
          const?App:?React.FC?=?()?=>?[1,?2,?3]
          const?App:?React.FC?=?()?=>?'hello'

          解決方法:

          const?App:?React.FC<{}>?=?props?=>?props.children?as?any
          const?App:?React.FC<{}>?=?()?=>?[1,?2,?3]?as?any
          const?App:?React.FC<{}>?=?()?=>?'hello'?as?any
          //?或者
          const?App:?React.FC<{}>?=?props?=>?(props.children?as?unknown)?as?JSX.Element
          const?App:?React.FC<{}>?=?()?=>?([1,?2,?3]?as?unknown)?as?JSX.Element
          const?App:?React.FC<{}>?=?()?=>?('hello'?as?unknown)?as?JSX.Element

          在通常情況下,使用 React.FC 的方式聲明最簡單有效,推薦使用;如果出現(xiàn)類型不兼容問題,建議使用以下兩種方式:

          第二種:使用 PropsWithChildren,這種方式可以為你省去頻繁定義 children 的類型,自動設(shè)置 children 類型為 ReactNode:

          type?AppProps?=?React.PropsWithChildren<{?message:?string?}>
          const?App?=?({?message,?children?}:?AppProps)?=>?(
          ??<div>
          ????{message}
          ????{children}
          ??div>

          )

          第三種:直接聲明:

          type?AppProps?=?{
          ??message:?string
          ??children?:?React.ReactNode
          }

          const?App?=?({?message,?children?}:?AppProps)?=>?(
          ??<div>
          ????{message}
          ????{children}
          ??div>

          )

          Hooks

          useState

          大部分情況下,TS 會自動為你推導(dǎo) state 的類型:

          //?`val`會推導(dǎo)為boolean類型,?toggle接收boolean類型參數(shù)
          const?[val,?toggle]?=?React.useState(false)
          //?obj會自動推導(dǎo)為類型:?{name:?string}
          const?[obj]?=?React.useState({?name:?'sj'?})
          //?arr會自動推導(dǎo)為類型:?string[]
          const?[arr]?=?React.useState(['One',?'Two'])

          使用推導(dǎo)類型作為接口/類型:

          export?default?function?App()?{
          ??//?user會自動推導(dǎo)為類型:?{name:?string}
          ??const?[user]?=?React.useState({?name:?'sj',?age:?32?})
          ??const?showUser?=?React.useCallback((obj:?typeof?user)?=>?{
          ????return?`My?name?is?${obj.name},?My?age?is?${obj.age}`
          ??},?[])
          ??return?<div?className="App">用戶:?{showUser(user)}div>
          }

          但是,一些狀態(tài)初始值為空時(null),需要顯示地聲明類型:

          type?User?=?{
          ??name:?string
          ??age:?number
          }
          const?[user,?setUser]?=?React.useStatenull>(null)

          useRef

          當(dāng)初始值為 null 時,有兩種創(chuàng)建方式:

          const?ref1?=?React.useRef(null)
          const?ref2?=?React.useRefnull>(null)

          這兩種的區(qū)別在于

          • 第一種方式的 ref1.current 是只讀的(read-only),并且可以傳遞給內(nèi)置的 ref 屬性,綁定 DOM 元素
          • 第二種方式的 ref2.current 是可變的(類似于聲明類的成員變量)
            const?ref?=?React.useRef(0)
            React.useEffect(()?=>?{
            ??ref.current?+=?1
            },?[])

            這兩種方式在使用時,都需要對類型進行檢查:

            const?onButtonClick?=?()?=>?{
            ??ref1.current?.focus()
            ??ref2.current?.focus()
            }

            在某種情況下,可以省去類型檢查,通過添加 ! 斷言不推薦

            //?Bad
            function?MyComponent()?{
            ??const?ref1?=?React.useRef(null!)
            ??React.useEffect(()?=>?{
            ????//??不需要做類型檢查,需要人為保證ref1.current.focus一定存在
            ????doSomethingWith(ref1.current.focus())
            ??})
            ??return?<div?ref={ref1}>?etc?div>
            }

            useEffect

            useEffect 需要注意回調(diào)函數(shù)的返回值只能是函數(shù)或者 undefined

            function?App()?{
            ??//?undefined作為回調(diào)函數(shù)的返回值
            ??React.useEffect(()?=>?{
            ????//?do?something...
            ??},?[])
            ??//?返回值是一個函數(shù)
            ??React.useEffect(()?=>?{
            ????//?do?something...
            ????return?()?=>?{}
            ??},?[])
            }

            useMemo / useCallback

            useMemouseCallback 都可以直接從它們返回的值中推斷出它們的類型

            useCallback 的參數(shù)必須制定類型,否則 ts 不會報錯,默認(rèn)指定為 any

            const?value?=?10
            //?自動推斷返回值為?number
            const?result?=?React.useMemo(()?=>?value?*?2,?[value])
            //?自動推斷?(value:?number)?=>?number
            const?multiply?=?React.useCallback((value:?number)?=>?value?*?multiplier,?[
            ??multiplier,
            ])

            同時也支持傳入泛型, useMemo 的泛型指定了返回值類型,useCallback 的泛型指定了參數(shù)類型

            //?也可以顯式的指定返回值類型,返回值不一致會報錯
            const?result?=?React.useMemo(()?=>?2,?[])
            //?類型“()?=> number”的參數(shù)不能賦給類型“()?=> string”的參數(shù)。
            const?handleChange?=?React.useCallback<
            ??React.ChangeEventHandler
            >(evt?=>?{
            ??console.log(evt.target.value)
            },?[])

            自定義 Hooks

            需要注意,自定義 Hook 的返回值如果是數(shù)組類型,TS 會自動推導(dǎo)為 Union 類型,而我們實際需要的是數(shù)組里里每一項的具體類型,需要手動添加 const 斷言 進行處理:

            function?useLoading()?{
            ??const?[isLoading,?setState]?=?React.useState(false)
            ??const?load?=?(aPromise:?Promise)?=>?{
            ????setState(true)
            ????return?aPromise.then(()?=>?setState(false))
            ??}
            ??//?實際需要:?[boolean,?typeof?load]?類型
            ??//?而不是自動推導(dǎo)的:(boolean | typeof load)[]
            ??return?[isLoading,?load]?as?const
            }

            如果使用 const 斷言遇到問題[7],也可以直接定義返回類型:

            export?function?useLoading():?[
            ??boolean,
            ??(aPromise:?Promise)?=>?Promise<any>
            ]?
            {
            ??const?[isLoading,?setState]?=?React.useState(false)
            ??const?load?=?(aPromise:?Promise)?=>?{
            ????setState(true)
            ????return?aPromise.then(()?=>?setState(false))
            ??}
            ??return?[isLoading,?load]
            }

            如果有大量的自定義 Hook 需要處理,這里有一個方便的工具方法可以處理 tuple 返回值:

            function?tuplify<T?extends?any[]>(...elements:?T)?{
            ??return?elements
            }
            function?useLoading()?{
            ??const?[isLoading,?setState]?=?React.useState(false)
            ??const?load?=?(aPromise:?Promise)?=>?{
            ????setState(true)
            ????return?aPromise.then(()?=>?setState(false))
            ??}

            ??//?(boolean?|?typeof?load)[]
            ??return?[isLoading,?load]
            }

            function?useTupleLoading()?{
            ??const?[isLoading,?setState]?=?React.useState(false)
            ??const?load?=?(aPromise:?Promise)?=>?{
            ????setState(true)
            ????return?aPromise.then(()?=>?setState(false))
            ??}

            ??//?[boolean,?typeof?load]
            ??return?tuplify(isLoading,?load)
            }

            默認(rèn)屬性 defaultProps

            大部分文章都不推薦使用 defaultProps , 相關(guān)討論可以點擊參考鏈接[8]

            推薦方式:使用默認(rèn)參數(shù)值來代替默認(rèn)屬性:

            type?GreetProps?=?{?age?:?number?}
            const?Greet?=?({?age?=?21?}:?GreetProps)?=>?{
            ??/*?...?*/
            }

            defaultProps 類型

            TypeScript3.0+[9] 在默認(rèn)屬性 的類型推導(dǎo)上有了極大的改進,雖然尚且存在一些邊界 case 仍然存在問題[10]不推薦使用,如果有需要使用的場景,可參照如下方式:

            type?IProps?=?{
            ??name:?string
            }
            const?defaultProps?=?{
            ??age:?25,
            }

            //?類型定義
            type?GreetProps?=?IProps?&?typeof?defaultProps
            const?Greet?=?(props:?GreetProps)?=>?<div>div>
            Greet.defaultProps?=?defaultProps
            //?使用
            const?TestComponent?=?(props:?React.ComponentProps<typeof?Greet>)?=>?{
            ??return?<h1?/>
            }
            const?el?=?<TestComponent?name="foo"?/>

            Types or Interfaces

            在日常的 react 開發(fā)中 interfacetype 的使用場景十分類似

            implementsextends 靜態(tài)操作,不允許存在一種或另一種實現(xiàn)的情況,所以不支持使用聯(lián)合類型:

            class?Point?{
            ??x:?number?=?2
            ??y:?number?=?3
            }
            interface?IShape?{
            ??area():?number
            }
            type?Perimeter?=?{
            ??perimeter():?number
            }
            type?RectangleShape?=?(IShape?|?Perimeter)?&?Point

            class?Rectangle?implements?RectangleShape?{
            ??//?類只能實現(xiàn)具有靜態(tài)已知成員的對象類型或?qū)ο箢愋偷慕患?/span>
            ??x?=?2
            ??y?=?3
            ??area()?{
            ????return?this.x?+?this.y
            ??}
            }
            interface?ShapeOrPerimeter?extends?RectangleShape?{}
            //?接口只能擴展使用靜態(tài)已知成員的對象類型或?qū)ο箢愋偷慕患?/span>

            使用 Type 還是 Interface?

            有幾種常用規(guī)則:

            • 在定義公共 API 時(比如編輯一個庫)使用 interface,這樣可以方便使用者繼承接口
            • 在定義組件屬性(Props)和狀態(tài)(State)時,建議使用 type,因為 type的約束性更強

            interfacetype 在 ts 中是兩個不同的概念,但在 React 大部分使用的 case 中,interfacetype 可以達到相同的功能效果,typeinterface 最大的區(qū)別是:

            • type 類型不能二次編輯,而 interface 可以隨時擴展
            interface?Animal?{
            ??name:?string
            }

            //?可以繼續(xù)在原有屬性基礎(chǔ)上,添加新屬性:color
            interface?Animal?{
            ??color:?string
            }
            /********************************/
            type?Animal?=?{
            ??name:?string
            }
            //?type類型不支持屬性擴展
            //?Error:?Duplicate?identifier?'Animal'
            type?Animal?=?{
            ??color:?string
            }

            獲取未導(dǎo)出的 Type

            某些場景下我們在引入第三方的庫時會發(fā)現(xiàn)想要使用的組件并沒有導(dǎo)出我們需要的組件參數(shù)類型或者返回值類型,這時候我們可以通過 ComponentProps/ ReturnType 來獲取到想要的類型。

            //?獲取參數(shù)類型
            import?{?Button?}?from?'library'?//?但是未導(dǎo)出props?type
            type?ButtonProps?=?React.ComponentProps<typeof?Button>?//?獲取props
            type?AlertButtonProps?=?Omit'onClick'>?//?去除onClick
            const?AlertButton:?React.FC?=?props?=>?(
            ??<Button?onClick={()?=>?alert('hello')}?{...props}?/>
            )
            //?獲取返回值類型
            function?foo()?{
            ??return?{?baz:?1?}
            }
            type?FooReturn?=?ReturnType<typeof?foo>?//?{?baz:?number?}

            Props

            通常我們使用 type 來定義 Props,為了提高可維護性和代碼可讀性,在日常的開發(fā)過程中我們希望可以添加清晰的注釋。

            現(xiàn)在有這樣一個 type

            type?OtherProps?=?{
            ??name:?string
            ??color:?string
            }

            在使用的過程中,hover 對應(yīng)類型會有如下展示

            //?type?OtherProps?=?{
            //???name:?string;
            //???color:?string;
            //?}
            const?OtherHeading:?React.FC?=?({?name,?color?})?=>?(
            ??<h1>My?Website?Headingh1>
            )

            增加相對詳細的注釋,使用時會更清晰,需要注意,注釋需要使用 /**/ // 無法被 vscode 識別

            //?Great
            /**
            ?*?@param?color?color
            ?*?@param?children?children
            ?*?@param?onClick?onClick
            ?*/


            type?Props?=?{
            ??/**?color?*/
            ??color?:?string
            ??/**?children?*/
            ??children:?React.ReactNode
            ??/**?onClick?*/
            ??onClick:?()?=>?void
            }

            //?type?Props
            //?@param?color?—?color
            //?@param?children?—?children
            //?@param?onClick?—?onClick
            const?Button:?React.FC?=?({?children,?color?=?'tomato',?onClick?})?=>?{
            ??return?(
            ????<button?style={{?backgroundColor:?color?}}?onClick={onClick}>
            ??????{children}
            ????button>

            ??)
            }

            常用 Props ts 類型

            基礎(chǔ)屬性類型

            type?AppProps?=?{
            ??message:?string
            ??count:?number
            ??disabled:?boolean
            ??/**?array?of?a?type!?*/
            ??names:?string[]
            ??/**?string?literals?to?specify?exact?string?values,?with?a?union?type?to?join?them?together?*/
            ??status:?'waiting'?|?'success'
            ??/**?任意需要使用其屬性的對象(不推薦使用,但是作為占位很有用)?*/
            ??obj:?object
            ??/**?作用和`object`幾乎一樣,和?`Object`完全一樣?*/
            ??obj2:?{}
            ??/**?列出對象全部數(shù)量的屬性?(推薦使用)?*/
            ??obj3:?{
            ????id:?string
            ????title:?string
            ??}
            ??/**?array?of?objects!?(common)?*/
            ??objArr:?{
            ????id:?string
            ????title:?string
            ??}[]
            ??/**?任意數(shù)量屬性的字典,具有相同類型*/
            ??dict1:?{
            ????[key:?string]:?MyTypeHere
            ??}
            ??/**?作用和dict1完全相同?*/
            ??dict2:?Record
            ??/**?任意完全不會調(diào)用的函數(shù)?*/
            ??onSomething:?Function
            ??/**?沒有參數(shù)&返回值的函數(shù)?*/
            ??onClick:?()?=>?void
            ??/**?攜帶參數(shù)的函數(shù)?*/
            ??onChange:?(id:?number)?=>?void
            ??/**?攜帶點擊事件的函數(shù)?*/
            ??onClick(event:?React.MouseEvent):?void
            ??/**?可選的屬性?*/
            ??optional?:?OptionalType
            }

            常用 React 屬性類型

            export?declare?interface?AppBetterProps?{
            ??children:?React.ReactNode?//?一般情況下推薦使用,支持所有類型?Great
            ??functionChildren:?(name:?string)?=>?React.ReactNode
            ??style?:?React.CSSProperties?//?傳遞style對象
            ??onChange?:?React.FormEventHandler
            }

            export?declare?interface?AppProps?{
            ??children1:?JSX.Element?//?差,?不支持?jǐn)?shù)組
            ??children2:?JSX.Element?|?JSX.Element[]?//?一般,?不支持字符串
            ??children3:?React.ReactChildren?//?忽略命名,不是一個合適的類型,工具類類型
            ??children4:?React.ReactChild[]?//?很好
            ??children:?React.ReactNode?//?最佳,支持所有類型?推薦使用
            ??functionChildren:?(name:?string)?=>?React.ReactNode?//?recommended?function?as?a?child?render?prop?type
            ??style?:?React.CSSProperties?//?傳遞style對象
            ??onChange?:?React.FormEventHandler?//?表單事件,?泛型參數(shù)是event.target的類型
            }

            Forms and Events

            onChange

            change 事件,有兩個定義參數(shù)類型的方法。

            第一種方法使用推斷的方法簽名(例如:React.FormEvent :void

            import?*?as?React?from?'react'

            type?changeFn?=?(e:?React.FormEvent)?=>?void
            const?App:?React.FC?=?()?=>?{
            ??const?[state,?setState]?=?React.useState('')
            ??const?onChange:?changeFn?=?e?=>?{
            ????setState(e.currentTarget.value)
            ??}
            ??return?(
            ????<div>
            ??????<input?type="text"?value={state}?onChange={onChange}?/>
            ????div>

            ??)
            }

            第二種方法強制使用 @types / react 提供的委托類型,兩種方法均可。

            import?*?as?React?from?'react'const?App:?React.FC?=?()?=>?{
            ??
            const?[state,?setState]?=?React.useState('')
            ??
            const?onChange:?React.ChangeEventHandler?=?e?=>?{
            ????setState(e.currentTarget.value)
            ??}
            ??
            return?(
            ????
            <div>
            ??????<input?type="text"?value={state}?onChange={onChange}?/>
            ????div>

            ??)
            }

            onSubmit

            如果不太關(guān)心事件的類型,可以直接使用 React.SyntheticEvent,如果目標(biāo)表單有想要訪問的自定義命名輸入,可以使用類型擴展


            import?*?as?React?from?'react'

            const?App:?React.FC?=?()?=>?{
            ??const?onSubmit?=?(e:?React.SyntheticEvent)?=>?{
            ????e.preventDefault()
            ????const?target?=?e.target?as?typeof?e.target?&?{
            ??????password:?{?value:?string?}
            ????}?//?類型擴展
            ????const?password?=?target.password.value
            ??}
            ??return?(
            ????<form?onSubmit={onSubmit}>
            ??????<div>
            ????????<label>
            ??????????Password:
            ??????????<input?type="password"?name="password"?/>
            ????????label>

            ??????div>
            ??????<div>
            ????????<input?type="submit"?value="Log?in"?/>
            ??????div>
            ????form>
            ??)
            }

            Operators

            常用的操作符,常用于類型判斷

            • typeof and instanceof: 用于類型區(qū)分
            • keyof: 獲取 object 的 key

            • O[K]: 屬性查找

            • [K in O]: 映射類型

            • + or - or readonly or ?: 加法、減法、只讀和可選修飾符

            • x ? Y : Z: 用于泛型類型、類型別名、函數(shù)參數(shù)類型的條件類型

            • !: 可空類型的空斷言

            • as: 類型斷言

            • is: 函數(shù)返回類型的類型保護

            Tips

            使用查找類型訪問組件屬性類型

            通過查找類型減少 type 的非必要導(dǎo)出,如果需要提供復(fù)雜的 type,應(yīng)當(dāng)提取到作為公共 API 導(dǎo)出的文件中。

            現(xiàn)在我們有一個 Counter 組件,需要 name 這個必傳參數(shù):

            //?counter.tsx
            import?*?as?React?from?'react'
            export?type?Props?=?{
            ??name:?string
            }
            const?Counter:?React.FC?=?props?=>?{
            ??return?<>
            }
            export?default?Counter

            在其他引用它的組件中我們有兩種方式獲取到 Counter 的參數(shù)類型

            第一種是通過 typeof 操作符(推薦

            //?Great
            import?Counter?from?'./d-tips1'
            type?PropsNew?=?React.ComponentProps<typeof?Counter>?&?{
            ??age:?number
            }
            const?App:?React.FC?=?props?=>?{
            ??return?<Counter?{...props}?/>
            }
            export?default?App

            第二種是通過在原組件進行導(dǎo)出

            import?Counter,?{?Props?}?from?'./d-tips1'
            type?PropsNew?=?Props?&?{
            ??age:?number
            }
            const?App:?React.FC?=?props?=>?{
            ??return?(
            ????<>
            ??????<Counter?{...props}?/>
            ????

            ??)
            }
            export?default?App

            不要在 type 或 interface 中使用函數(shù)聲明

            保持一致性,類型/接口的所有成員都通過相同的語法定義。

            --strictFunctionTypes 在比較函數(shù)類型時強制執(zhí)行更嚴(yán)格的類型檢查,但第一種聲明方式下嚴(yán)格檢查不生效。

            ?

            interface?ICounter?{
            ??start:?(value:?number)?=>?string
            }

            ?

            interface?ICounter1?{
            ??start(value:?number):?string
            }



            ??

            interface?Animal?{}
            interface?Dog?extends?Animal?{
            ??wow:?()?=>?void
            }
            interface?Comparer?{
            ??compare:?(a:?T,?b:?T)?=>?number
            }
            declare?let?animalComparer:?Comparer
            declare?let?dogComparer:?Comparer
            animalComparer?=?dogComparer?//?Error
            dogComparer?=?animalComparer?//?Ok
            interface?Comparer1?{
            ??compare(a:?T,?b:?T):?number
            }
            declare?let?animalComparer1:?Comparer1
            declare?let?dogComparer1:?Comparer1
            animalComparer1?=?dogComparer?//?Ok
            dogComparer1?=?animalComparer?//?Ok

            事件處理

            我們在進行事件注冊時經(jīng)常會在事件處理函數(shù)中使用 event 事件對象,例如當(dāng)使用鼠標(biāo)事件時我們通過 clientXclientY 去獲取指針的坐標(biāo)。

            大家可能會想到直接把 event 設(shè)置為 any 類型,但是這樣就失去了我們對代碼進行靜態(tài)檢查的意義。

            function?handleEvent(event:?any)?{、
            ??console.log(event.clientY)
            }

            試想下當(dāng)我們注冊一個 Touch 事件,然后錯誤的通過事件處理函數(shù)中的 event 對象去獲取其 clientY 屬性的值,在這里我們已經(jīng)將 event 設(shè)置為 any 類型,導(dǎo)致 TypeScript 在編譯時并不會提示我們錯誤, 當(dāng)我們通過 event.clientY 訪問時就有問題了,因為 Touch 事件的 event 對象并沒有 clientY 這個屬性。

            通過 interfaceevent 對象進行類型聲明編寫的話又十分浪費時間,幸運的是 React 的聲明文件提供了 Event 對象的類型聲明。

            Event 事件對象類型

            • ClipboardEvent 剪切板事件對象
            • DragEvent 拖拽事件對象

            • ChangeEvent Change 事件對象

            • KeyboardEvent 鍵盤事件對象

            • MouseEvent 鼠標(biāo)事件對象

            • TouchEvent 觸摸事件對象

            • WheelEvent 滾輪時間對象

            • AnimationEvent 動畫事件對象

            • TransitionEvent 過渡事件對象

            事件處理函數(shù)類型

            當(dāng)我們定義事件處理函數(shù)時有沒有更方便定義其函數(shù)類型的方式呢?答案是使用 React 聲明文件所提供的 EventHandler 類型別名,通過不同事件的 EventHandler 的類型別名來定義事件處理函數(shù)的類型

            type?EventHandler>?=?{
            ??bivarianceHack(event:?E):?void
            }['bivarianceHack']
            type?ReactEventHandler?=?EventHandler>
            type?ClipboardEventHandler?=?EventHandler>
            type?DragEventHandler?=?EventHandler>
            type?FocusEventHandler?=?EventHandler>
            type?FormEventHandler?=?EventHandler>
            type?ChangeEventHandler?=?EventHandler>
            type?KeyboardEventHandler?=?EventHandler>
            type?MouseEventHandler?=?EventHandler>
            type?TouchEventHandler?=?EventHandler>
            type?PointerEventHandler?=?EventHandler>
            type?UIEventHandler?=?EventHandler>
            type?WheelEventHandler?=?EventHandler>
            type?AnimationEventHandler?=?EventHandler>
            type?TransitionEventHandler?=?EventHandler<
            ??React.TransitionEvent
            >

            bivarianceHack 為事件處理函數(shù)的類型定義,函數(shù)接收一個 event 對象,并且其類型為接收到的泛型變量 E 的類型, 返回值為 void

            關(guān)于為何是用 bivarianceHack 而不是(event: E): void,這與 strictfunctionTypes 選項下的功能兼容性有關(guān)。(event: E): void,如果該參數(shù)是派生類型,則不能將其傳遞給參數(shù)是基類的函數(shù)。

            class?Animal?{
            ??private?x:?undefined
            }
            class?Dog?extends?Animal?{
            ??private?d:?undefined
            }
            type?EventHandler?=?(event:?E)?=>?void
            let?z:?EventHandler?=?(o:?Dog)?=>?{}?//?fails?under?strictFunctionTyes
            type?BivariantEventHandler?=?{
            ??bivarianceHack(event:?E):?void
            }['bivarianceHack']
            let?y:?BivariantEventHandler?=?(o:?Dog)?=>?{}

            Promise 類型

            在做異步操作時我們經(jīng)常使用 async 函數(shù),函數(shù)調(diào)用時會 return 一個 Promise 對象,可以使用 then 方法添加回調(diào)函數(shù)。Promise 是一個泛型類型,T 泛型變量用于確定 then 方法時接收的第一個回調(diào)函數(shù)的參數(shù)類型。

            type?IResponse?=?{
            ??message:?string
            ??result:?T
            ??success:?boolean
            }
            async?function?getResponse():?Promise<IResponse<number[]>>?{
            ??return?{
            ????message:?'獲取成功',
            ????result:?[1,?2,?3],
            ????success:?true,
            ??}
            }

            getResponse().then(response?=>?{
            ??console.log(response.result)
            })

            首先聲明 IResponse 的泛型接口用于定義 response 的類型,通過 T 泛型變量來確定 result 的類型。然后聲明了一個 異步函數(shù) getResponse 并且將函數(shù)返回值的類型定義為 Promise> 。最后調(diào)用 getResponse 方法會返回一個 promise 類型,通過 then 調(diào)用,此時 then 方法接收的第一個回調(diào)函數(shù)的參數(shù) response 的類型為,{ message: string, result: number[], success: boolean}

            泛型參數(shù)的組件

            下面這個組件的 name 屬性都是指定了傳參格式,如果想不指定,而是想通過傳入?yún)?shù)的類型去推導(dǎo)實際類型,這就要用到泛型。

            const?TestB?=?({?name,?name2?}:?{?name:?string;?name2?:?string?})?=>?{
            ??return?(
            ????<div?className="test-b">
            ??????TestB--{name}
            ??????{name2}
            ????div>

            ??)
            }

            如果需要外部傳入?yún)?shù)類型,只需 ->

            type?Props?=?{
            ??name:?T
            ??name2?:?T
            }
            const?TestC:?(props:?Props)?=>?React.ReactElement?=?({?name,?name2?})?=>?{
            ??return?(
            ????
            ??????TestB--{name}
            ??????{name2}
            ????

            ??)
            }

            const?TestD?=?()?=>?{
            ??return?(
            ????

            ???????name="123"?/>
            ????

            ??)
            }

            什么時候使用泛型

            當(dāng)你的函數(shù),接口或者類:

            • 需要作用到很多類型的時候,舉個 ??

            當(dāng)我們需要一個 id 函數(shù),函數(shù)的參數(shù)可以是任何值,返回值就是將參數(shù)原樣返回,并且其只能接受一個參數(shù),在 js 時代我們會很輕易地甩出一行

            const?id?=?arg?=>?arg

            由于其可以接受任意值,也就是說我們的函數(shù)的入?yún)⒑头祷刂刀紤?yīng)該可以是任意類型,如果不使用泛型,我們只能重復(fù)的進行定義

            type?idBoolean?=?(arg:?boolean)?=>?boolean
            type?idNumber?=?(arg:?number)?=>?number
            type?idString?=?(arg:?string)?=>?string
            //?...

            如果使用泛型,我們只需要

            function?id<T>(arg:?T):?T?{
            ??return?arg
            }

            //?或
            const?id1:?<T>(arg:?T)?=>?T?=?arg?=>?{
            ??return?arg
            }
            • 需要被用到很多地方的時候,比如常用的工具泛型 Partial

            功能是將類型的屬性變成可選, 注意這是淺 Partial

            type?Partial?=?{?[P?in?keyof?T]?:?T[P]?}

            如果需要深 Partial 我們可以通過泛型遞歸來實現(xiàn)

            type?DeepPartial?=?T?extends?Function
            ????T
            ??:?T?extends?object
            ????{?[P?in?keyof?T]?:?DeepPartial?}
            ??:?T
            type?PartialedWindow?=?DeepPartial

            參考資料

            [1]

            2ality's guide: http://2ality.com/2018/04/type-notation-typescript.html

            [2]

            chibicode's tutorial: https://ts.chibicode.com/todo/

            [3]

            TS 部分: https://reactjs.org/docs/static-type-checking.html#typescript

            [4]

            React 部分: http://www.typescriptlang.org/play/index.html?jsx=2&esModuleInterop=true&e=181#example/typescript-with-react

            [5]

            被證明: https://www.reddit.com/r/reactjs/comments/iyehol/import_react_from_react_will_go_away_in_distant/

            [6]

            一些 issue: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33006

            [7]

            問題: https://github.com/babel/babel/issues/9800

            [8]

            參考鏈接: https://twitter.com/hswolff/status/1133759319571345408

            [9]

            TypeScript3.0+: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html

            [10]

            存在一些邊界 case 仍然存在問題: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/61

            瀏覽 37
            點贊
            評論
            收藏
            分享

            手機掃一掃分享

            分享
            舉報
            評論
            圖片
            表情
            推薦
            點贊
            評論
            收藏
            分享

            手機掃一掃分享

            分享
            舉報
            <kbd id="afajh"><form id="afajh"></form></kbd>
            <strong id="afajh"><dl id="afajh"></dl></strong>
              <del id="afajh"><form id="afajh"></form></del>
                  1. <th id="afajh"><progress id="afajh"></progress></th>
                    <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    亚洲三级在线 | 麻豆成人久久精品 | 午夜精品影院 | 国产做受 91电影 | 久久99精品波多野结衣一区 |