let name: string = 'John'
let age: number = 30
let isAdmin: boolean = false
let tags: string[] = ['react', 'ts']
let tuple: [string, number] = ['id', 1]
let anything: any = 'avoid this'
let unknownValue: unknown
let nullable: string | null = null
type ButtonProps = {
label: string
disabled?: boolean
}
function Button({ label, disabled }: ButtonProps) {
return <button disabled={disabled}>{label}</button>
}
type CardProps = {
title: string
children: React.ReactNode
}
function Card({ title, children }: CardProps) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
)
}
type Props = {
onClick: () => void
onChange: (value: string) => void
}
type Props = {
name?: string
}
type Props = {
status: 'loading' | 'success' | 'error'
}
const [count, setCount] = useState<number>(0)
const [name, setName] = useState<string>('')
const [user, setUser] = useState<User | null>(null)
const [items, setItems] = useState<string[]>([])
type User = {
id: number
name: string
}
const [user, setUser] = useState<User>({
id: 1,
name: 'John',
})
const [user, setUser] = useState<User | null>(null)
Avoid:
useState({})
const inputRef = useRef<HTMLInputElement>(null)
const timerRef = useRef<number | null>(null)
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
console.log(e.target.value)
}
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
console.log('clicked')
}
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
}
useEffect(() => {
const timer = setInterval(() => {}, 1000)
return () => clearInterval(timer)
}, [])
No special typing usually needed.
type AuthContextType = {
user: string | null
login: () => void
}
const AuthContext = createContext<AuthContextType | null>(null)
const auth = useContext(AuthContext)
if (!auth) {
throw new Error('AuthContext missing')
}
type State = {
count: number
}
type Action = { type: 'increment' } | { type: 'decrement' }
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
default:
return state
}
}
async function fetchUsers(): Promise<User[]> {
const res = await fetch('/api/users')
return res.json()
}
type ApiResponse<T> = {
data: T
error: string | null
}
type User = {
id: number
name: string
}
const response: ApiResponse<User[]> = {
data: [],
error: null,
}
type ListProps<T> = {
items: T[]
renderItem: (item: T) => React.ReactNode
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <div>{items.map(renderItem)}</div>
}
Makes all fields optional.
type User = {
id: number
name: string
}
type PartialUser = Partial<User>
Select specific fields.
type UserPreview = Pick<User, 'id' | 'name'>
Remove fields.
type UserWithoutId = Omit<User, 'id'>
Dictionary type.
const users: Record<string, number> = {
john: 1,
jane: 2,
}
Usually inferred automatically.
Optional explicit typing:
function App(): JSX.Element {
return <div>Hello</div>
}
function useCounter(initial: number) {
const [count, setCount] = useState(initial)
const increment = () => setCount((c) => c + 1)
return { count, increment }
}
Usually inference is enough.
type User = {
id: number
}
interface User {
id: number
}
type → unions, utility compositioninterface → extendable object contractsModern React code commonly prefers type.
type Props = {
title: string
}
function Header({ title }: Props) {
return <h1>{title}</h1>
}
function Header({ title }: { title: string }) {
return <h1>{title}</h1>
}
Good for very small components only.
type Props = {
size?: number
}
function Avatar({ size = 100 }: Props) {
return <div>{size}</div>
}
React.ReactNode
React.ReactElement
React.FC
React.Dispatch
React.SetStateAction
anyconst data: any
Prefer:
const data: unknown
Usually unnecessary.
Prefer:
function Button() {}
Instead of:
const Button: React.FC = () => {}
Avoid:
const input = document.getElementById('x') as HTMLInputElement
type Props = {}
useState<Type>()
React.ChangeEvent<HTMLInputElement>
useRef<HTMLInputElement>(null)
Promise<Type>
string
number
boolean
Type | null
Type[]
React.ReactNode
React.ChangeEvent<HTMLInputElement>
React.MouseEvent<HTMLButtonElement>
useState<Type>()
useRef<Type>(null)
Promise<Type>