日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

?? 準(zhǔn)備知識(shí)

  • 熟悉 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,簡(jiǎn)寫形式: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 對(duì)靜態(tài)屬性:displayName、propTypes、defaultProps 提供了類型檢查和自動(dòng)補(bǔ)全
  • React.FC 為 children 提供了隱式的類型(ReactElement | null),但是目前,提供的類型存在一些 issue[6](問(wèn)題)

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

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 的方式聲明最簡(jiǎn)單有效,推薦使用;如果出現(xiàn)類型不兼容問(wèn)題,建議使用以下兩種方式:

第二種:使用 PropsWithChildren,這種方式可以為你省去頻繁定義 children 的類型,自動(dòng)設(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 會(huì)自動(dòng)為你推導(dǎo) state 的類型:

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

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

export default function App() {
  // user會(huì)自動(dòng)推導(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)初始值為空時(shí)(null),需要顯示地聲明類型:

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

useRef<T>

當(dāng)初始值為 null 時(shí),有兩種創(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
}, [])

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

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

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

// 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...
  }, [])
  // 返回值是一個(gè)函數(shù)
  React.useEffect(() => {
    // do something...
    return () => {}
  }, [])
}

useMemo<T> / useCallback<T>

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

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

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

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

// 也可以顯式的指定返回值類型,返回值不一致會(huì)報(bào)錯(cuò)
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 會(huì)自動(dòng)推導(dǎo)為 Union 類型,而我們實(shí)際需要的是數(shù)組里里每一項(xiàng)的具體類型,需要手動(dò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òng)推導(dǎo)的:(boolean | typeof load)[]
  return [isLoading, load] as const
}

如果使用 const 斷言遇到問(wèn)題[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 需要處理,這里有一個(gè)方便的工具方法可以處理 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ù)值來(lái)代替默認(rèn)屬性:

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

defaultProps 類型

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

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ā)中 interface 和 type 的使用場(chǎng)景十分類似

implements 與 extends 靜態(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)已知成員的對(duì)象類型或?qū)ο箢愋偷慕患?  x = 2
  y = 3
  area() {
    return this.x + this.y
  }
}
interface ShapeOrPerimeter extends RectangleShape {}
// 接口只能擴(kuò)展使用靜態(tài)已知成員的對(duì)象類型或?qū)ο箢愋偷慕患?/code>

使用 Type 還是 Interface?

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

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

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

  • type 類型不能二次編輯,而 interface 可以隨時(shí)擴(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

某些場(chǎng)景下我們?cè)谝氲谌降膸?kù)時(shí)會(huì)發(fā)現(xiàn)想要使用的組件并沒有導(dǎo)出我們需要的組件參數(shù)類型或者返回值類型,這時(shí)候我們可以通過(guò) ComponentProps/ ReturnType 來(lái)獲取到想要的類型。

// 獲取參數(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 { baz: 1 }
}
type FooReturn = ReturnType<typeof foo> // { baz: number }

Props

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

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

type OtherProps = {
  name: string
  color: string
}

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

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

增加相對(duì)詳細(xì)的注釋,使用時(shí)會(huì)更清晰,需要注意,注釋需要使用 /**/  // 無(wú)法被 vscode 識(shí)別

// 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'
  /** 任意需要使用其屬性的對(duì)象(不推薦使用,但是作為占位很有用) */
  obj: object
  /** 作用和`object`幾乎一樣,和 `Object`完全一樣 */
  obj2: {}
  /** 列出對(duì)象全部數(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>
  /** 任意完全不會(huì)調(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對(duì)象
  onChange?: React.FormEventHandler<HTMLInputElement>
}

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

Forms and Events

onChange

change 事件,有兩個(gè)定義參數(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)表單有想要訪問(wèn)的自定義命名輸入,可以使用類型擴(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

使用查找類型訪問(wèn)組件屬性類型

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

現(xiàn)在我們有一個(gè) Counter 組件,需要 name 這個(gè)必傳參數(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ù)類型

第一種是通過(guò) 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

第二種是通過(guò)在原組件進(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ù)聲明

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

--strictFunctionTypes 在比較函數(shù)類型時(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

事件處理

我們?cè)谶M(jìn)行事件注冊(cè)時(shí)經(jīng)常會(huì)在事件處理函數(shù)中使用 event 事件對(duì)象,例如當(dāng)使用鼠標(biāo)事件時(shí)我們通過(guò) clientXclientY 去獲取指針的坐標(biāo)。

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

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

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

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

Event 事件對(duì)象類型

  • ClipboardEvent<T = Element> 剪切板事件對(duì)象
  • DragEvent<T =Element> 拖拽事件對(duì)象
  • ChangeEvent<T = Element> Change 事件對(duì)象
  • KeyboardEvent<T = Element> 鍵盤事件對(duì)象
  • MouseEvent<T = Element> 鼠標(biāo)事件對(duì)象
  • TouchEvent<T = Element> 觸摸事件對(duì)象
  • WheelEvent<T = Element> 滾輪時(shí)間對(duì)象
  • AnimationEvent<T = Element> 動(dòng)畫事件對(duì)象
  • TransitionEvent<T = Element> 過(guò)渡事件對(duì)象

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

當(dāng)我們定義事件處理函數(shù)時(shí)有沒有更方便定義其函數(shù)類型的方式呢?答案是使用 React 聲明文件所提供的 EventHandler 類型別名,通過(guò)不同事件的 EventHandler 的類型別名來(lái)定義事件處理函數(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ù)接收一個(gè) event 對(duì)象,并且其類型為接收到的泛型變量 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 類型

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

type IResponse<T> = {
  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 的類型,通過(guò) T 泛型變量來(lái)確定 result 的類型。然后聲明了一個(gè) 異步函數(shù) getResponse 并且將函數(shù)返回值的類型定義為 Promise<IResponse<number[]>> 。最后調(diào)用 getResponse 方法會(huì)返回一個(gè) promise 類型,通過(guò) then 調(diào)用,此時(shí) then 方法接收的第一個(gè)回調(diào)函數(shù)的參數(shù) response 的類型為,{ message: string, result: number[], success: boolean} 。

泛型參數(shù)的組件

下面這個(gè)組件的 name 屬性都是指定了傳參格式,如果想不指定,而是想通過(guò)傳入?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>
  )
}

什么時(shí)候使用泛型

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

  • 需要作用到很多類型的時(shí)候,舉個(gè)

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

const id = arg => arg

由于其可以接受任意值,也就是說(shuō)我們的函數(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
}
  • 需要被用到很多地方的時(shí)候,比如常用的工具泛型 Partial

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

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

如果需要深 Partial 我們可以通過(guò)泛型遞歸來(lái)實(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>

 

參考資料

[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]問(wèn)題:
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 仍然存在問(wèn)題:
https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/61

本文轉(zhuǎn)載自前端大全

分享到:
標(biāo)簽:TypeScript
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定