This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/REACT/3. 生态工具篇/10-TS + React.md
T
2026-04-29 22:19:51 +08:00

5.7 KiB
Raw Blame History

tags, create time
tags create time
React
TypeScript
Frontend
2026-04-29 22:09

TS + React

概述

TypeScript 为 React 提供编译时类型检查和智能提示。本文档系统梳理 Props、State、Hook、Ref 等核心场景的类型定义方式,以及进阶的泛型推导模式。

Props 类型定义

基础方式

// 方式1:interface(推荐,可 extend)
interface ButtonProps {
  label: string;
  onClick?: () => void;
}
const Button = ({ label, onClick }: ButtonProps) => <button onClick={onClick}>{label}</button>;

// 方式2:type alias
type ButtonProps = { label: string; onClick?: () => void };

// ⚠️ 避免:函数参数解构后不再标注
// 这样会导致每个参数无法被单独推断
function Component({ a, b }) { ... }  // any!

合成事件类型

// ❌ 不要用 HTML 原生的 Event
const handleChange = (e: Event) => {};

// ✅ 用 React 的合成事件类型
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  e.target.value;  // string | number | string[]
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
};

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  console.log(e.button);  // 鼠标按键
};

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.key === "Enter") submit();
};

Children 类型

// 通用 children 类型
function Card({ children }: { children: React.ReactNode }) {}

// 严格类型 children(限制允许的子节点类型)
interface TabsProps {
  children: React.ReactElement<TabProps>;  // 只能是 Tab 组件
}

// 数组形式
interface ListProps<T> {
  children: React.ReactElement<{ item: T }> [];
}

State 类型推导

interface User { name: string; role: "admin" | "user"; age: number }

// 完整泛型
const [user, setUser] = useState<User>({ name: "", role: "user", age: 0 });

// 可选初始值
const [user, setUser] = useState<User | null>(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 类型定义

// 元素 ref
const inputRef = useRef<HTMLInputElement>(null);

// mutable value ref(不触发 re-render)
const timerIdRef = useRef<ReturnType<typeof setInterval>>(undefined);
timerIdRef.current = setInterval(() => {}, 1000);

// class component style ref object(用于挂载子组件引用)
const childRef = useRef<ComponentInstance>(null);
// 需要 useImperativeHandle 暴露方法给父级

// forwardRef 中 ref 的正确用法
const FancyInput = forwardRef<HTMLInputElement, { label: string }>(function FancyInput(props, ref) {
  const innerRef = useRef<HTMLInputElement>(null);
  
  useImperativeHandle(ref, () => ({
    focus: () => innerRef.current?.focus(),
    blur: () => innerRef.current?.blur(),
  }));
  
  return <input ref={innerRef} label={props.label} />;
});

Hook 类型推导

// 自定义 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<T, Args extends any[]>(
  asyncFn: (...args: Args) => Promise<T>,
  deps: DependencyList
): { data: T | null; loading: boolean; error: Error | null; invoke: (...args: Args) => void } {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(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(区分联合类型)

// 类型守卫 —— 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" };
  }
}
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

关联笔记