--- tags: [React, TypeScript, Frontend] create time: 2026-04-29 22:09 --- # TS + React ## 概述 TypeScript 为 React 提供编译时类型检查和智能提示。本文档系统梳理 Props、State、Hook、Ref 等核心场景的类型定义方式,以及进阶的泛型推导模式。 ## Props 类型定义 ### 基础方式 ```tsx // 方式1:interface(推荐,可 extend) interface ButtonProps { label: string; onClick?: () => void; } const Button = ({ label, onClick }: ButtonProps) => ; // 方式2:type alias type ButtonProps = { label: string; onClick?: () => void }; // ⚠️ 避免:函数参数解构后不再标注 // 这样会导致每个参数无法被单独推断 function Component({ a, b }) { ... } // any! ``` ### 合成事件类型 ```tsx // ❌ 不要用 HTML 原生的 Event const handleChange = (e: Event) => {}; // ✅ 用 React 的合成事件类型 const handleChange = (e: React.ChangeEvent) => { e.target.value; // string | number | string[] }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); }; const handleClick = (e: React.MouseEvent) => { console.log(e.button); // 鼠标按键 }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") submit(); }; ``` ### Children 类型 ```tsx // 通用 children 类型 function Card({ children }: { children: React.ReactNode }) {} // 严格类型 children(限制允许的子节点类型) interface TabsProps { children: React.ReactElement; // 只能是 Tab 组件 } // 数组形式 interface ListProps { children: React.ReactElement<{ item: T }> []; } ``` ## State 类型推导 ```tsx interface User { name: string; role: "admin" | "user"; age: number } // 完整泛型 const [user, setUser] = useState({ name: "", role: "user", age: 0 }); // 可选初始值 const [user, setUser] = useState(null); // 使用时需判空 user?.name; // 安全 (user as User).name; // or user!?.name; // non-null assertion // useReducer 类型推导 interface State { items: Item[]; filter: string } type Action = { type: "SET_FILTER"; payload: string } | { type: "ADD_ITEM"; payload: Item }; function reducer(state: State, action: Action): State { switch (action.type) { case "SET_FILTER": return { ...state, filter: action.payload }; case "ADD_ITEM": return { ...state, items: [...state.items, action.payload] }; } } const [state, dispatch] = useReducer(reducer, initialState); ``` ## Ref 类型定义 ```tsx // 元素 ref const inputRef = useRef(null); // mutable value ref(不触发 re-render) const timerIdRef = useRef>(undefined); timerIdRef.current = setInterval(() => {}, 1000); // class component style ref object(用于挂载子组件引用) const childRef = useRef(null); // 需要 useImperativeHandle 暴露方法给父级 // forwardRef 中 ref 的正确用法 const FancyInput = forwardRef(function FancyInput(props, ref) { const innerRef = useRef(null); useImperativeHandle(ref, () => ({ focus: () => innerRef.current?.focus(), blur: () => innerRef.current?.blur(), })); return ; }); ``` ## Hook 类型推导 ```tsx // 自定义 Hook 返回值类型 interface UseCountReturn { count: number; increment: () => void; decrement: () => void; } function useCount(initial = 0): UseCountReturn { const [count, setCount] = useState(initial); return { count, increment: () => setCount(c => c + 1), decrement: () => setCount(c => c - 1), }; } // generic hook —— 最强大的类型推导场景 function useAsync( asyncFn: (...args: Args) => Promise, deps: DependencyList ): { data: T | null; loading: boolean; error: Error | null; invoke: (...args: Args) => void } { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const invoke = useCallback(async (...args: Args) => { setLoading(true); setError(null); try { const result = await asyncFn(...args); setData(result); } catch (err) { setError(err as Error); } finally { setLoading(false); } }, [asyncFn, ...deps]); useEffect(() => { invoke(); }, [invoke]); return { data, loading, error, invoke }; } // 使用:T 自动推导! const { data: users } = useAsync(fetchUsers, []); // data: User[] | null const { data: user } = useAsync(fetchUserById, [id]); // data: User | null ``` ## discriminated Union(区分联合类型) ```tsx // 类型守卫 —— React 状态管理中最常用的类型模式 interface SuccessAction { type: "success"; data: User[] } interface ErrorAction { type: "error"; error: string } interface LoadingAction { type: "loading" } type Action = SuccessAction | ErrorAction | LoadingAction; function reducer(state: State, action: Action): State { switch (action.type) { case "success": return { ...state, data: action.data, status: "loaded" }; // ✅ TS 自动推断 action.data 是 User[] case "error": return { ...state, error: action.error }; // ✅ TS 自动推断 action.error 是 string case "loading": return { ...state, status: "loading" }; } } ``` ```mermaid graph LR A["Union Type"] -->|"switch / if"| B["Type Narrowing"] B --> C["discriminated union: type field"] B --> D["typeof check"] B --> E["in operator"] style C fill:#61DAFB,color:#000 ``` ## 关联笔记