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'; // 可選類型自動包含 undefinedsize?: '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>
);