基础介绍

基本类型

type BasicTypes = {
  message: string
  count: number
  disabled: boolean
  names: string[] // or Array<string>
  id: string | number // 联合类型
}

联合类型

  • 例如:自定义 ajax 时,一般 method 就那么具体的几种:getpostput 等。 大家都知道需要传入一个 string 型,你可能会这么写:
type Method = string // ❎ bad code 这样可以传入任意字符串

type Method = 'get' | 'post' | 'put' // ✅ good code  这样就只能传入这三种字符串了

对象类型

  • 一般知道属性类型的话,可以这么写:
type ObjectTypes = {
  obj3: {
    id: string
    title: string
  }
  objArr: {
    id: string
    title: string
  }[] // 对象数组,or Array<{ id: string, title: string }>
}
  • 如果不知道类型你可能会这样写
type ObjectTypes = {
  obj: object // ❎ bad code 这样写的话,obj 就可以是任意类型了
  obj2: {} // ❎ bad code 这样写的话 obj2 就只能是空对象了
}
  • 正确的写法,推荐使用 Record 来定义对象类型
type ObjectTypes = {
  obj: Record<string, unknown> // ✅ good code 这样写的话,obj 就可以是任意类型了 可以替换 obj: object
  obj2: Record<string, never> // ✅ good code 这样写的话 obj2 就只能是空对象了
  obj3: unknown // ✅ good code 这样写的话 obj3 就可以是任意类型了

  // 以上三种写法都可以,推荐使用 Record
  dict1: {
    // 对象索引签名
    [key: string]: MytypeHere
  }

  dict2: Record<string, MytypeHere> // 等价于 dict1
}

函数类型

  • 函数类型不建议直接给 Function 类型,有明确的参数类型、个数与返回值类型最佳。
type FunctionTypes = {
  // ❎ bad code
  func: Function

  // ✅ good code
  func2: (a: number, b: number) => number
}

可选属性

  • 有时候我们定义的对象,不是所有属性都是必须的,这时候可以使用 ? 来表示可选属性,例如在 react 中的 props 就是可选属性
type AppProps = {
  name: string
  age?: number
  [propName: string]: any
}
const YourComponent = ({ name, age, ...other }: AppProps) => (
  <div>
    {`Hello, my name is ${name}, ${age || 'unknown'}`}
    <Other {...other} />
  </div>
)

React Prop 类型

  • React 中的 props 有很多类型,这里列举几个常用的
export declare interface AppProps {
  children1: JSX.Element // ❎bad, 没有考虑数组类型
  children2: JSX.Element | JSX.Element[] // ❎ 没考虑字符类型
  children3: React.ReactChildren // ❎ 名字唬人,工具类型,慎用
  children4: React.ReactChild[] // better, 但没考虑 null
  children: React.ReactNode // ✅ best, 最佳接收所有 children 类型
  functionChildren: (name: string) => React.ReactNode // ✅ 返回 React 节点
  style?: React.CSSProperties // React style
  onChange?: React.FormEventHandler<HTMLInputElement> // 表单事件! 泛型参数即 `event.target` 的类型
}

函数式组件

  • 函数式组件的类型定义,一般是这样的
type AppProps = { message: string } /* 也可用 interface */
const App = ({ message }: AppProps) => <div>{message}</div> // 无大括号的箭头函数,利用 TS 推断。
  • 也可以使用 React.FC 来定义函数式组件
type AppProps = { title: string }
const App: React.FC<AppProps> = ({ children, title }) => (
  <div title={title}>{children}</div>
)

Hooks

useState

  • useState 的类型定义
type AppProps = { message: string }
const App = () => {
  const [data] = useState<AppProps | null>(null)
  // const [data] = useState<AppProps | undefined>();
  return <div>{data && data.message}</div>
}

// data && data.message  可以使用 链式操作符 ?. 代替: data?.message

useEffect

  • useEffect 的类型定义
type AppProps = { message: string }
const App = ({ message }: AppProps) => {
  useEffect(() => {
    console.log(message)

    // 或者使用 then
    axios.get('https://api.github.com').then((result) => {
      console.log(result)
    })

    // 处理异步请求
    const fetchData = async () => {
      const result = await axios.get('https://api.github.com')
      console.log(result)
    }
    fetchData() // 也可以使用 async await
    ;(async () => {
      const result = await axios.get('https://api.github.com')
      console.log(result)
    })()
  }, [message]) // ✅ OK
  return <div>{message}</div>
}

useRef

  • useRef: 用于获取 DOM 节点,或者保存变量
const ref1 = useRef<HTMLElement>(null!)
const ref2 = useRef<HTMLElement | null>(null)

function MyComponent() {
  const ref1 = useRef<HTMLElement>(null!)
  useEffect(() => {
    doSomethingWith(ref1.current)
    // 跳过 TS null 检查。e.g. ref1 && ref1.current
  })
  return <div ref={ref1}> etc </div>
}

//  不太建议使用,因为 ref2.current 可能为 null
// 推荐使用
function TextInputWithFocusButton() {
  // 初始化为 null, 但告知 TS 是希望 HTMLInputElement 类型
  // inputEl 只能用于 input elements
  const inputEl = React.useRef<HTMLInputElement>(null)
  const onButtonClick = () => {
    // TS 会检查 inputEl 类型,初始化 null 是没有 current 上是没有 focus 属性的
    // 你需要自定义判断!
    if (inputEl && inputEl.current) {
      inputEl.current.focus()
    }
    // ✅ best
    inputEl.current?.focus()
  }
  return (
    <>
      <input ref={inputEl} type='text' />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  )
}

useReducers

  • useReducers 的类型定义
const initialState = { count: 0 }

// ❌ bad,可能传入未定义的 type 类型,或码错单词,而且还需要针对不同的 type 来兼容 payload
// type ACTIONTYPE = { type: string; payload?: number | string };

// ✅ good
type ACTIONTYPE =
  | { type: 'increment'; payload: number }
  | { type: 'decrement'; payload: string }
  | { type: 'initial' }

function reducer(state: typeof initialState, action: ACTIONTYPE) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + action.payload }
    case 'decrement':
      return { count: state.count - Number(action.payload) }
    case 'initial':
      return { count: initialState.count }
    default:
      throw new Error()
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement', payload: '5' })}>
        -
      </button>
      <button onClick={() => dispatch({ type: 'increment', payload: 5 })}>
        +
      </button>
    </>
  )
}

useContext

  • useContext 的类型定义
interface AppContextInterface {
  state: typeof initialState
  dispatch: React.Dispatch<ACTIONTYPE>
}
// 创建 context
const AppCtx = React.createContext<AppContextInterface>({
  state: initialState,
  dispatch: (action) => action,
})

// 使用 context
const App = (): React.ReactNode => {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <AppCtx.Provider value={{ state, dispatch }}>
      <Counter />
    </AppCtx.Provider>
  )
}

// 消费 context
function Counter() {
  const { state, dispatch } = React.useContext(AppCtx)
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement', payload: '5' })}>
        -
      </button>
      <button onClick={() => dispatch({ type: 'increment', payload: 5 })}>
        +
      </button>
    </>
  )
}

参考资料