Advanced TypeScript and React Patterns: Importing React, Functional Components, Hooks, Props, and Types
This guide covers the essential TypeScript knowledge for React development, demonstrating how to import React, declare functional components with React.FC, use core hooks such as useState, useRef, useEffect, useMemo, create custom hooks, handle defaultProps, choose between type and interface, type props, manage forms and events, work with operators, and apply generic patterns for reusable components.
Preparation Knowledge : Familiarity with React, TypeScript (e.g., 2ality's guide and chibicode's tutorial), the React official documentation (TS section), and the TypeScript Playground (React section) is required.
How to Import React :
import * as React from 'react'
import * as ReactDOM from 'react-dom'The above import style is considered the most reliable and is recommended. An alternative import style requires the compiler option allowSyntheticDefaultImports: true :
import React from 'react'
import ReactDOM from 'react-dom'Functional Component Declaration :
Recommended way is using React.FC (or React.FunctionComponent ) with explicit return type:
type AppProps = { message: string }
const App: React.FC
= ({ message, children }) => (
{message}{children}
)Differences between React.FC and plain function declarations include explicit return‑type checking, static properties support, and implicit children typing (which may have issues).
When type incompatibility occurs, you can cast with as any or use unknown and then assert to JSX.Element .
Two alternative declaration styles are:
Using React.PropsWithChildren to automatically add children?: ReactNode .
Directly declaring props with a type and using a regular function component.
Hooks :
useState<T>
TypeScript infers state types automatically; you can also explicitly annotate:
const [val, toggle] = React.useState(false) // boolean
const [obj] = React.useState({ name: 'sj' }) // { name: string }
const [arr] = React.useState(['One', 'Two']) // string[]When the initial state is null , you must provide a generic type:
type User = { name: string; age: number }
const [user, setUser] = React.useState
(null)useRef<T>
Two ways to create a ref with a null initial value:
const ref1 = React.useRef
(null) // read‑only, can be passed to
ref
prop
const ref2 = React.useRef
(null) // mutableBoth require type checking before use; you can bypass with non‑null assertion ( ! ) but it is discouraged.
useEffect
The effect callback may return either a cleanup function or undefined only.
React.useEffect(() => {
// do something
}, []) // returns undefined
React.useEffect(() => {
// do something
return () => {}
}, []) // returns cleanup functionuseMemo & useCallback
Both infer return types automatically; you can explicitly specify generic types to enforce return or parameter types.
const result = React.useMemo(() => value * 2, [value]) // number
const multiply = React.useCallback((value: number) => value * multiplier, [multiplier])
const typed = React.useMemo
(() => 'text', []) // error if returns numberCustom Hooks
When a custom hook returns a tuple, TypeScript may infer a union type; use as const or explicit return type to preserve element types.
function useLoading() {
const [isLoading, setState] = React.useState(false)
const load = (aPromise: Promise
) => {
setState(true)
return aPromise.then(() => setState(false))
}
return [isLoading, load] as const
}A helper tuplify can be used to keep tuple types.
function tuplify
(...elements: T) { return elements }
function useLoading() { /* same as above */ }
return tuplify(isLoading, load)Default Props :
Using defaultProps is generally discouraged; prefer default parameter values in the component signature.
type GreetProps = { age?: number }
const Greet = ({ age = 21 }: GreetProps) => { /* ... */ }When needed, you can type defaultProps with typeof and intersect it with the component’s prop type.
Types vs Interfaces
Use interface for public APIs that may be extended; use type for component props because it provides stricter constraints. Interfaces can be merged, while types cannot.
interface Animal { name: string }
interface Animal { color: string } // merged
type Animal = { name: string }
// type Animal = { color: string } // error: duplicate identifierYou can extract non‑exported types via React.ComponentProps or ReturnType :
type ButtonProps = React.ComponentProps
type FooReturn = ReturnTypeProps Typing :
Define props with type , add JSDoc comments ( /** */ ) for better IDE hints.
/**
* @param color color
* @param children children
* @param onClick click handler
*/
type Props = {
/** color */
color?: string
/** children */
children: React.ReactNode
/** onClick */
onClick: () => void
}Common prop types include primitives, arrays, objects, dictionaries ( Record<string, T> ), and event handlers.
Forms and Events
For onChange you can type the handler as React.ChangeEventHandler<HTMLInputElement> or define a custom type.
type ChangeFn = (e: React.FormEvent<HTMLInputElement>) => void
const onChange: ChangeFn = e => setState(e.currentTarget.value)For onSubmit you can use React.SyntheticEvent and extend the target type to access custom fields.
const onSubmit = (e: React.SyntheticEvent) => {
e.preventDefault()
const target = e.target as typeof e.target & { password: { value: string } }
const password = target.password.value
}Operators
Common TypeScript operators used in React typings include typeof , keyof , indexed access O[K] , mapped types [K in O] , conditional types T extends U ? X : Y , non‑null assertion ! , type assertions as , and type‑guard functions is .
Tips
Use typeof Component to retrieve component props ( React.ComponentProps<typeof Counter> ) or export the prop type directly.
When building generic components, define a generic prop type and let the consumer specify the concrete type:
type Props
= { name: T; name2?: T }
const TestC = <T,>(props: Props
) => (
TestC--{props.name}{props.name2}
)
// usageUse generics for utilities such as Partial<T> or deep partials:
type Partial
= { [P in keyof T]?: T[P] }
type DeepPartial
= T extends Function ? T : T extends object ? { [P in keyof T]?: DeepPartial
} : TRecruitment Notice :
The ByteDance “懂车帝” team is hiring front‑end engineers proficient in React and TypeScript; contact [email protected] with the subject “应聘+城市+岗位名称”.
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.