React与TypeScript #

一、TypeScript配置 #

1.1 安装 #

bash
npm install -D typescript @types/react @types/react-dom

1.2 tsconfig.json #

json
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

二、组件类型 #

2.1 函数组件 #

typescript
import { ReactNode } from 'react';

// 基本组件
function Button({ children }: { children: ReactNode }) {
  return <button>{children}</button>;
}

// 使用接口定义Props
interface ButtonProps {
  children: ReactNode;
  variant?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  onClick?: () => void;
}

function Button({
  children,
  variant = 'primary',
  size = 'medium',
  disabled = false,
  onClick
}: ButtonProps) {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

export default Button;

2.2 使用FC类型 #

typescript
import { FC, ReactNode } from 'react';

interface CardProps {
  title: string;
  children: ReactNode;
}

const Card: FC<CardProps> = ({ title, children }) => {
  return (
    <div className="card">
      <h2>{title}</h2>
      <div className="card-body">{children}</div>
    </div>
  );
};

2.3 泛型组件 #

typescript
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => ReactNode;
  keyExtractor: (item: T) => string | number;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map(item => (
        <li key={keyExtractor(item)}>
          {renderItem(item)}
        </li>
      ))}
    </ul>
  );
}

// 使用
<List
  items={users}
  renderItem={(user) => <span>{user.name}</span>}
  keyExtractor={(user) => user.id}
/>

三、常用类型 #

3.1 React类型 #

typescript
import {
  ReactNode,
  ReactElement,
  JSXElementConstructor,
  FunctionComponent,
  Component,
  ComponentProps,
  RefObject,
  MutableRefObject
} from 'react';

// ReactNode - 可以渲染的内容
type ReactNode = 
  | ReactElement 
  | string 
  | number 
  | ReactFragment 
  | ReactPortal 
  | boolean 
  | null 
  | undefined;

// ReactElement - React元素
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
  type: T;
  props: P;
  key: Key | null;
}

3.2 事件类型 #

typescript
import {
  ChangeEvent,
  FormEvent,
  MouseEvent,
  KeyboardEvent,
  FocusEvent,
  DragEvent
} from 'react';

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

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };

  const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
    console.log(e.clientX, e.clientY);
  };

  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      console.log('Enter pressed');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input onChange={handleChange} onKeyDown={handleKeyDown} />
      <button onClick={handleClick}>Submit</button>
    </form>
  );
}

3.3 Ref类型 #

typescript
import { useRef, RefObject, MutableRefObject } from 'react';

function Input() {
  // 只读ref
  const inputRef: RefObject<HTMLInputElement> = useRef(null);
  
  // 可变ref
  const timerRef: MutableRefObject<NodeJS.Timeout | null> = useRef(null);

  const focus = () => {
    inputRef.current?.focus();
  };

  return <input ref={inputRef} />;
}

四、Hooks类型 #

4.1 useState #

typescript
// 基本类型
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>('');
const [isActive, setIsActive] = useState<boolean>(false);

// 对象类型
interface User {
  id: number;
  name: string;
  email: string;
}

const [user, setUser] = useState<User | null>(null);

// 数组类型
const [items, setItems] = useState<string[]>([]);
const [users, setUsers] = useState<User[]>([]);

// 惰性初始化
const [state, setState] = useState<User>(() => {
  const saved = localStorage.getItem('user');
  return saved ? JSON.parse(saved) : defaultUser;
});

4.2 useEffect #

typescript
useEffect(() => {
  // 副作用逻辑
  const timer = setInterval(() => {
    console.log('tick');
  }, 1000);

  return () => {
    clearInterval(timer);
  };
}, []);

4.3 useRef #

typescript
// DOM引用
const inputRef = useRef<HTMLInputElement>(null);
const divRef = useRef<HTMLDivElement>(null);

// 任意值引用
const timerRef = useRef<NodeJS.Timeout | null>(null);
const countRef = useRef<number>(0);

4.4 useMemo & useCallback #

typescript
const memoizedValue = useMemo<string>(() => {
  return computeExpensiveValue(a, b);
}, [a, b]);

const memoizedCallback = useCallback(
  (id: number, name: string) => {
    doSomething(id, name);
  },
  [dependency]
);

4.5 useContext #

typescript
interface ThemeContextType {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

4.6 useReducer #

typescript
interface State {
  count: number;
}

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'set'; payload: number };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'set':
      return { count: action.payload };
    default:
      return state;
  }
}

const [state, dispatch] = useReducer(reducer, { count: 0 });

五、组件模式 #

5.1 条件渲染 #

typescript
interface UserCardProps {
  user: User | null;
}

function UserCard({ user }: UserCardProps) {
  if (!user) {
    return <div>No user data</div>;
  }

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

5.2 列表渲染 #

typescript
interface UserListProps {
  users: User[];
  onUserClick: (user: User) => void;
}

function UserList({ users, onUserClick }: UserListProps) {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id} onClick={() => onUserClick(user)}>
          {user.name}
        </li>
      ))}
    </ul>
  );
}

5.3 表单处理 #

typescript
interface FormData {
  email: string;
  password: string;
}

function LoginForm() {
  const [formData, setFormData] = useState<FormData>({
    email: '',
    password: ''
  });

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };

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

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="email"
        type="email"
        value={formData.email}
        onChange={handleChange}
      />
      <input
        name="password"
        type="password"
        value={formData.password}
        onChange={handleChange}
      />
      <button type="submit">Login</button>
    </form>
  );
}

六、高级类型 #

6.1 组件Props类型 #

typescript
// 基础Props
type BaseProps = {
  className?: string;
  style?: React.CSSProperties;
  children?: ReactNode;
};

// 按钮Props
type ButtonProps = BaseProps & {
  variant?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
};

// 输入框Props
type InputProps = BaseProps & {
  value: string;
  onChange: (e: ChangeEvent<HTMLInputElement>) => void;
  placeholder?: string;
  type?: 'text' | 'password' | 'email';
};

6.2 提取组件Props类型 #

typescript
import { ComponentProps } from 'react';

// 获取HTML元素Props
type ButtonProps = ComponentProps<'button'>;
type InputProps = ComponentProps<'input'>;

// 获取组件Props
type MyButtonProps = ComponentProps<typeof Button>;

6.3 联合类型 #

typescript
type Status = 'idle' | 'loading' | 'success' | 'error';

interface State {
  status: Status;
  data: User | null;
  error: string | null;
}

function UserView({ state }: { state: State }) {
  switch (state.status) {
    case 'idle':
      return <div>Click to load</div>;
    case 'loading':
      return <div>Loading...</div>;
    case 'success':
      return <div>{state.data?.name}</div>;
    case 'error':
      return <div>Error: {state.error}</div>;
  }
}

七、类型声明文件 #

7.1 全局类型 #

typescript
// types/global.d.ts
declare global {
  interface Window {
    customProperty: string;
  }
}

export {};

7.2 模块类型 #

typescript
// types/user.d.ts
export interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
}

export interface UserResponse {
  data: User;
  message: string;
}

7.3 第三方库类型 #

typescript
// types/styled.d.ts
declare module 'styled-components' {
  export interface DefaultTheme {
    colors: {
      primary: string;
      secondary: string;
      background: string;
      text: string;
    };
    fonts: {
      main: string;
      code: string;
    };
  }
}

八、最佳实践 #

8.1 类型定义位置 #

typescript
// 小型组件:类型定义在组件文件内
interface ButtonProps {
  children: ReactNode;
}

export function Button({ children }: ButtonProps) {
  return <button>{children}</button>;
}

// 大型组件/共享类型:类型定义在单独文件
// types/user.ts
export interface User { /* ... */ }

// components/UserCard.tsx
import { User } from '@/types/user';

8.2 避免any #

typescript
// ❌ 使用any
const handleData = (data: any) => {
  console.log(data.name);
};

// ✅ 使用具体类型
interface Data {
  name: string;
  age: number;
}

const handleData = (data: Data) => {
  console.log(data.name);
};

// ✅ 使用unknown进行类型守卫
const handleUnknown = (data: unknown) => {
  if (typeof data === 'object' && data !== null && 'name' in data) {
    console.log((data as { name: string }).name);
  }
};

8.3 使用类型推断 #

typescript
// ✅ 让TypeScript推断
const [count, setCount] = useState(0);

// ✅ 复杂类型显式声明
const [user, setUser] = useState<User | null>(null);

九、总结 #

要点 说明
组件类型 使用interface定义Props
事件类型 使用React提供的事件类型
Hooks类型 useState、useRef等需要类型
泛型组件 处理通用组件类型

核心原则:

  • 为Props定义明确的类型
  • 避免使用any
  • 利用类型推断
  • 使用类型守卫
最后更新:2026-03-26