1. 字節(jié) React + TypeScript 實(shí)踐總結(jié)篇

        共 29877字,需瀏覽 60分鐘

         ·

        2021-07-27 12:04

        ?? 準(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<AppProps> = ({ message, children }) => (
          <div>
            {message}
            {children}
          </div>

        )

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

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

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

        比如以下用法 React.FC 會報(bào)類型錯誤:

        const App: React.FC = props => props.children
        const App: React.FC = () => [123]
        const App: React.FC = () => 'hello'

        解決方法:

        const App: React.FC<{}> = props => props.children as any
        const App: React.FC<{}> = () => [123as any
        const App: React.FC<{}> = () => 'hello' as any
        // 或者
        const App: React.FC<{}> = props => (props.children as unknown) as JSX.Element
        const App: React.FC<{}> = () => ([123as 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<T>

        大部分情況下,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'age32 })
          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.useState<User | null>(null)

        useRef<T>

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

        const ref1 = React.useRef<HTMLInputElement>(null)
        const ref2 = React.useRef<HTMLInputElement | null>(null)

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

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

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

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

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

          // Bad
          function MyComponent({
            const ref1 = React.useRef<HTMLDivElement>(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<T> / useCallback<T>

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

          useCallback 的參數(shù)必須制定類型,否則 ts 不會報(bào)錯,默認(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ù)類型

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

          自定義 Hooks

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

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

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

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

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

          默認(rèn)屬性 defaultProps

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

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

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

          defaultProps 類型

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

          type IProps = {
            name: string
          }
          const defaultProps = {
            age25,
          }

          // 類型定義
          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)操作,不允許存在一種或另一種實(shí)現(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 {
            // 類只能實(shí)現(xiàn)具有靜態(tài)已知成員的對象類型或?qū)ο箢愋偷慕患?/span>
            x = 2
            y = 3
            area() {
              return this.x + this.y
            }
          }
          interface ShapeOrPerimeter extends RectangleShape {}
          // 接口只能擴(kuò)展使用靜態(tài)已知成員的對象類型或?qū)ο箢愋偷慕患?/span>

          使用 Type 還是 Interface?

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

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

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

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

          // 可以繼續(xù)在原有屬性基礎(chǔ)上,添加新屬性:color
          interface Animal {
            color: string
          }
          /********************************/
          type Animal = {
            name: string
          }
          // type類型不支持屬性擴(kuò)展
          // 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<ButtonProps, 'onClick'// 去除onClick
          const AlertButton: React.FC<AlertButtonProps> = props => (
            <Button onClick={() => alert('hello')} {...props} />
          )
          // 獲取返回值類型
          function foo({
            return { baz1 }
          }
          type FooReturn = ReturnType<typeof foo> // { baz: number }

          Props

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

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

          type OtherProps = {
            name: string
            color: string
          }

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

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

          增加相對詳細(xì)的注釋,使用時會更清晰,需要注意,注釋需要使用 /**/ , // 無法被 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<Props> = ({ 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<string, MyTypeHere>
            /** 任意完全不會調(diào)用的函數(shù) */
            onSomething: Function
            /** 沒有參數(shù)&返回值的函數(shù) */
            onClick() => void
            /** 攜帶參數(shù)的函數(shù) */
            onChange(id: number) => void
            /** 攜帶點(diǎn)擊事件的函數(shù) */
            onClick(event: React.MouseEvent<HTMLButtonElement>): void
            /** 可選的屬性 */
            optional?: OptionalType
          }

          常用 React 屬性類型

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

          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<HTMLInputElement> // 表單事件, 泛型參數(shù)是event.target的類型
          }

          Forms and Events

          onChange

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

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

          import * as React from 'react'

          type changeFn = (e: React.FormEvent<HTMLInputElement>) => 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>

            )
          }

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

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

            )
          }

          onSubmit

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


          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 }
              } // 類型擴(kuò)展
              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ù)返回類型的類型保護(hù)

          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> = 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<PropsNew> = props => {
            return <Counter {...props} />
          }
          export default App

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

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

            )
          }
          export default App

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

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

          --strictFunctionTypes 在比較函數(shù)類型時強(qiáng)制執(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<T> {
            compare(a: T, b: T) => number
          }
          declare let animalComparer: Comparer<Animal>
          declare let dogComparer: Comparer<Dog>
          animalComparer = dogComparer // Error
          dogComparer = animalComparer // Ok
          interface Comparer1<T> {
            compare(a: T, b: T): number
          }
          declare let animalComparer1: Comparer1<Animal>
          declare let dogComparer1: Comparer1<Dog>
          animalComparer1 = dogComparer // Ok
          dogComparer1 = animalComparer // Ok

          事件處理

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

          大家可能會想到直接把 event 設(shè)置為 any 類型,但是這樣就失去了我們對代碼進(jìn)行靜態(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 訪問時就有問題了,因?yàn)?Touch 事件的 event 對象并沒有 clientY 這個屬性。

          通過 interfaceevent 對象進(jìn)行類型聲明編寫的話又十分浪費(fèi)時間,幸運(yùn)的是 React 的聲明文件提供了 Event 對象的類型聲明。

          Event 事件對象類型

          • ClipboardEvent<T = Element> 剪切板事件對象
          • DragEvent<T =Element> 拖拽事件對象

          • ChangeEvent<T = Element> Change 事件對象

          • KeyboardEvent<T = Element> 鍵盤事件對象

          • MouseEvent<T = Element> 鼠標(biāo)事件對象

          • TouchEvent<T = Element> 觸摸事件對象

          • WheelEvent<T = Element> 滾輪時間對象

          • AnimationEvent<T = Element> 動畫事件對象

          • TransitionEvent<T = Element> 過渡事件對象

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

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

          type EventHandler<E extends React.SyntheticEvent<any>> = {
            bivarianceHack(event: E): void
          }['bivarianceHack']
          type ReactEventHandler<T = Element> = EventHandler<React.SyntheticEvent<T>>
          type ClipboardEventHandler<T = Element> = EventHandler<React.ClipboardEvent<T>>
          type DragEventHandler<T = Element> = EventHandler<React.DragEvent<T>>
          type FocusEventHandler<T = Element> = EventHandler<React.FocusEvent<T>>
          type FormEventHandler<T = Element> = EventHandler<React.FormEvent<T>>
          type ChangeEventHandler<T = Element> = EventHandler<React.ChangeEvent<T>>
          type KeyboardEventHandler<T = Element> = EventHandler<React.KeyboardEvent<T>>
          type MouseEventHandler<T = Element> = EventHandler<React.MouseEvent<T>>
          type TouchEventHandler<T = Element> = EventHandler<React.TouchEvent<T>>
          type PointerEventHandler<T = Element> = EventHandler<React.PointerEvent<T>>
          type UIEventHandler<T = Element> = EventHandler<React.UIEvent<T>>
          type WheelEventHandler<T = Element> = EventHandler<React.WheelEvent<T>>
          type AnimationEventHandler<T = Element> = EventHandler<React.AnimationEvent<T>>
          type TransitionEventHandler<T = Element> = EventHandler<
            React.TransitionEvent<T>
          >

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

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

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

          Promise 類型

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

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

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

          首先聲明 IResponse 的泛型接口用于定義 response 的類型,通過 T 泛型變量來確定 result 的類型。然后聲明了一個 異步函數(shù) getResponse 并且將函數(shù)返回值的類型定義為 Promise<IResponse<number[]>> 。最后調(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)實(shí)際類型,這就要用到泛型。

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

            )
          }

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

          type Props<T> = {
            name: T
            name2?: T
          }
          const TestC: <T>(props: Props<T>) => React.ReactElement = ({ name, name2 }) => {
            return (
              <div className="test-b">
                TestB--{name}
                {name2}
              </div>
            )
          }

          const TestD = () => {
            return (
              <div>
                <TestC<string> name="123" />
              </div>
            )
          }

          什么時候使用泛型

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

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

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

          const id = arg => arg

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

          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<T> = { [P in keyof T]?: T[P] }

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

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

          字節(jié)跳動懂車帝團(tuán)隊(duì)招聘

          我們是字節(jié)跳動旗下懂車帝產(chǎn)品線,目前業(yè)務(wù)上正處于高速發(fā)展階段,懂車帝自 2017 年 8 月正式誕生,僅三年時間已經(jīng)是汽車互聯(lián)網(wǎng)行業(yè)第二。

          現(xiàn)在前端團(tuán)隊(duì)主流的技術(shù)棧是 React、Typescript,主要負(fù)責(zé)懂車帝 App、M 站、PC 站、懂車帝小程序產(chǎn)品矩陣、商業(yè)化海量業(yè)務(wù),商業(yè)數(shù)據(jù)產(chǎn)品等。我們在類客戶端、多宿主、技術(shù)建站、中后臺系統(tǒng)、全棧、富交互等多種應(yīng)用場景都有大量技術(shù)實(shí)踐,致力于技術(shù)驅(qū)動業(yè)務(wù)發(fā)展,探索所有可能性。

          加入懂車帝,一起打造汽車領(lǐng)域最專業(yè)最開放的前端團(tuán)隊(duì)!

          簡歷直達(dá)[email protected]

          郵件標(biāo)題:應(yīng)聘+城市+崗位名稱

          如果覺得這篇文章還不錯
          點(diǎn)擊下面卡片關(guān)注我
          來個【分享、點(diǎn)贊、在看】三連支持一下吧

             “分享、點(diǎn)贊在看” 支持一波 

          參考資料

          [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

          瀏覽 49
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
            
            

              1. 另类小说亚洲区欧美动图无码第页 | 少妇高潮表情100图 | 校花喂我乳我脱她内裤第章 | 久久久日韩精品无码一区二区 | 青青草黄色视频 |