TypeScript 为 React 开发带来了强大的类型安全保障,这里解析常见的一些TS写法:
一、组件基础类型
1. 函数组件定义
// 显式声明 Props 类型并标注返回值
interface WelcomeProps {
name: string;
age?: number; // 可选属性
}
const Welcome: React.FC<WelcomeProps> = ({ name, age = 18 }) => (
<div>
Hello {name}, 年龄 {age}
</div>
);
// React 18+ 需要显式声明 children(如果用到)
interface CardProps {
children: React.ReactNode;
}
const Card: React.FC<CardProps> = ({ children }) => (
<div className="card">{children}</div>
)
2. 类组件写法
type CounterState = { count: number };
class Counter extends React.Component<{}, CounterState> {
state: CounterState = { count: 0 };
handleClick = () => {
this.setState(prev => ({ count: prev.count + 1 }));
};
render() {
return <button onClick={this.handleClick}>点击次数:{this.state.count}</button>;
}
}
二、Props 高级用法
1. 联合类型与类型守卫
type User = {
id: number;
type: 'admin' | 'user';
email?: string;
accessLevel?: number;
};
const UserProfile = ({ user }: { user: User }) => {
if (user.type === 'admin') {
return <div>管理员权限:{user.accessLevel}</div>;
}
return <div>用户邮箱:{user.email}</div>;
};
2. 默认值与类型推断
interface ButtonProps {
type?: 'primary' | 'dashed'; // 可选类型自动包含 undefined
size?: 'large' | 'medium';
}
const MyButton = ({ type = 'primary', size = 'medium' }: ButtonProps) => (
<button className={`${type} ${size}`}>按钮</button>
);
三、Hooks 类型实战
1. useState 精确控制
const [user, setUser] = useState<User | null>(null); // 联合类型处理异步数据
// 明确数组项类型
const [todos, setTodos] = useState<{ id: string; text: string }[]>([]);
2. useRef 双重用法
// 操作 DOM
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus(); // 需要非空校验
}
});
// 保存可变值
const timerRef = useRef<number>();
timerRef.current = setInterval(() => {});
3. useContext 类型安全
type Theme = 'light' | 'dark';
const ThemeContext = createContext<Theme>('light');
const App = () => (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
const Child = () => {
const theme = useContext(ThemeContext); // 自动推断为 Theme 类型
return <div className={theme}>当前主题</div>;
}
四、事件处理与泛型组件
1. 表单事件精准捕获
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
<input onChange={handleChange} />
// 鼠标事件
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
const rect = e.currentTarget.getBoundingClientRect(); // 正确访问 DOM 属性
};
2. 让组件更灵活:泛型组件
interface ListProps<T> {
data: T[];
renderItem: (item: T) => React.ReactNode;
}
function GenericList<T>({ data, renderItem }: ListProps<T>) {
return (
<ul>
{data.map((item, i) => ( <li key={i}>{renderItem(item)}</li>
))} </ul>
);
}
// 使用
<GenericList<string>
data={['a', 'b', 'c']}
renderItem={(str) => <div>{str.toUpperCase()}</div>}
/>
五、常见类型问题速查
// 1. 类型断言:谨慎使用
const element = document.getElementById('root') as HTMLElement;
// 2. 处理可能未初始化的 ref
const videoRef = useRef<HTMLVideoElement>(null!); // 初始化后使用可使用非空断言
// 3. 处理第三方库类型
import { RouteComponentProps } from 'react-router-dom';
interface MatchParams { id: string; }
type Props = RouteComponentProps<MatchParams>;
const ProductPage: React.FC<Props> = ({ match }) => (
<div>商品ID:{match.params.id}</div>
);