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