Frontend Development 23 min read

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.

ByteFE
ByteFE
ByteFE
Advanced TypeScript and React Patterns: Importing React, Functional Components, Hooks, Props, and Types

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) // mutable

Both 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 function

useMemo & 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 number

Custom 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 identifier

You can extract non‑exported types via React.ComponentProps or ReturnType :

type ButtonProps = React.ComponentProps
type FooReturn = ReturnType

Props 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}
)
// usage

Use 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
} : T

Recruitment Notice :

The ByteDance “懂车帝” team is hiring front‑end engineers proficient in React and TypeScript; contact [email protected] with the subject “应聘+城市+岗位名称”.

typescriptHooksEvent HandlingPropsFunctional Components
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.