TypeScript 基础回顾
1. 基本类型
typescript
// 基础类型
const name: string = "Alice";
const age: number = 25;
const isStudent: boolean = true;
const anything: any = "can be anything";
const nothing: null = null;
const notDefined: undefined = undefined;
// 数组类型
const numbers: number[] = [1, 2, 3];
const strings: Array<string> = ["a", "b", "c"];
const mixed: (string | number)[] = [1, "two", 3];
// 元组
const tuple: [string, number] = ["hello", 42];
// 联合类型
type Status = "pending" | "success" | "error";
const status: Status = "pending";
// 字面量类型
type Direction = "up" | "down" | "left" | "right";2. 接口与类型
typescript
// 接口 (interface)
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读属性
}
// 类型别名 (type)
type Product = {
id: number;
name: string;
price: number;
};
// 接口继承
interface AdminUser extends User {
role: "admin" | "user";
permissions: string[];
}
// 区别:interface 可以声明合并,type 不行
interface User {
phone?: string; // 可以再次声明 User
}3. 函数类型
typescript
// 函数类型注解
function add(a: number, b: number): number {
return a + b;
}
// 箭头函数
const multiply = (a: number, b: number): number => a * b;
// 可选参数和默认参数
function greet(name: string, greeting: string = "Hello"): void {
console.log(`${greeting}, ${name}`);
}
// 函数类型定义
type Callback = (data: string) => void;
const handleCallback: Callback = (data) => console.log(data);
// 重载
function process(input: string): string;
function process(input: number): number;
function process(input: string | number): string | number {
if (typeof input === "string") {
return input.toUpperCase();
}
return input * 2;
}4. 泛型
typescript
// 基础泛型
function getFirst<T>(arr: T[]): T {
return arr[0];
}
// 使用泛型
const firstNum = getFirst([1, 2, 3]); // type: number
const firstStr = getFirst(["a", "b"]); // type: string
// 泛型约束
interface HasId {
id: number;
}
function printId<T extends HasId>(obj: T): void {
console.log(obj.id);
}
// 多个泛型
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
// 泛型类
class Container<T> {
private data: T;
constructor(value: T) {
this.data = value;
}
getValue(): T {
return this.data;
}
}TypeScript + React 组件类型
5. 函数组件类型定义
5.1 基础函数组件
typescript
import { FC, ReactNode } from 'react';
// 方式1:使用 FC (FunctionComponent) 类型
const Welcome: FC<{ name: string }> = ({ name }) => {
return <div>Welcome, {name}</div>;
};
// 方式2:定义 Props 接口后使用
interface ButtonProps {
label: string;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
disabled?: boolean;
className?: string;
}
const Button: FC<ButtonProps> = ({ label, onClick, disabled, className }) => {
return (
<button onClick={onClick} disabled={disabled} className={className}>
{label}
</button>
);
};
// 使用组件
<Button label="Click me" onClick={() => console.log('clicked')} />5.2 带子元素的组件
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-content">{children}</div>
</div>
);
};
// 使用
<Card title="My Card">
<p>This is content</p>
</Card>5.3 事件处理类型
typescript
interface InputProps {
value: string;
onChange: (value: string) => void;
onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
}
const Input: FC<InputProps> = ({ value, onChange, onBlur }) => {
return (
<input
value={value}
onChange={(e) => onChange(e.target.value)}
onBlur={onBlur}
/>
);
};
// 常见事件类型
type ClickEvent = React.MouseEvent<HTMLButtonElement>;
type ChangeEvent = React.ChangeEvent<HTMLInputElement>;
type FormEvent = React.FormEvent<HTMLFormElement>;
type KeyboardEvent = React.KeyboardEvent<HTMLInputElement>;6. Hooks 类型
6.1 useState 类型
typescript
import { useState } from "react";
// 方式1:类型自动推断
const [count, setCount] = useState(0); // type: number
// 方式2:显式指定类型
const [name, setName] = useState<string>("");
const [user, setUser] = useState<User | null>(null);
// 方式3:复杂对象
interface AppState {
user: User | null;
isLoading: boolean;
error: string | null;
}
const [state, setState] = useState<AppState>({
user: null,
isLoading: false,
error: null,
});
// 更新状态
setState((prev) => ({ ...prev, isLoading: true }));6.2 useEffect 类型
typescript
import { useEffect } from "react";
// 基础 useEffect
useEffect(() => {
const timer = setTimeout(() => {
console.log("done");
}, 1000);
return () => clearTimeout(timer); // 清理函数
}, []); // 依赖数组
// 异步操作
useEffect(() => {
let isMounted = true;
const fetchData = async () => {
try {
const response = await fetch("/api/users");
const data: User[] = await response.json();
if (isMounted) {
setUsers(data);
}
} catch (error) {
if (isMounted) {
setError(error as Error);
}
}
};
fetchData();
return () => {
isMounted = false;
};
}, []);6.3 useReducer 类型
typescript
import { useReducer } from 'react';
// 定义 State 和 Action 类型
interface CounterState {
count: number;
history: number[];
}
type CounterAction =
| { type: 'INCREMENT' }
| { type: 'DECREMENT' }
| { type: 'RESET' }
| { type: 'SET_VALUE'; payload: number };
const initialState: CounterState = {
count: 0,
history: []
};
function counterReducer(state: CounterState, action: CounterAction): CounterState {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1,
history: [...state.history, state.count + 1]
};
case 'DECREMENT':
return {
...state,
count: state.count - 1,
history: [...state.history, state.count - 1]
};
case 'SET_VALUE':
return { ...state, count: action.payload };
case 'RESET':
return initialState;
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'SET_VALUE', payload: 10 })}>Set 10</button>
</div>
);
}6.4 useContext 类型
typescript
import { createContext, useContext, FC, ReactNode } from 'react';
// 定义 Context 类型
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
// Provider 组件
interface ThemeProviderProps {
children: ReactNode;
}
const ThemeProvider: FC<ThemeProviderProps> = ({ children }) => {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const value: ThemeContextType = {
theme,
toggleTheme: () => setTheme(prev => prev === 'light' ? 'dark' : 'light')
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
};
// 自定义 Hook(推荐使用方式)
function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// 使用
function Header() {
const { theme, toggleTheme } = useTheme();
return <button onClick={toggleTheme}>Current: {theme}</button>;
}6.5 useRef 类型
typescript
import { useRef } from 'react';
// 引用 DOM 元素
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus();
};
// 引用非 DOM 值
const countRef = useRef<number>(0);
const increment = () => {
countRef.current++;
console.log(countRef.current);
};
// 使用
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>7. 自定义 Hooks 类型
7.1 useForm Hook
typescript
interface FormState<T> {
values: T;
errors: Partial<Record<keyof T, string>>;
touched: Partial<Record<keyof T, boolean>>;
}
interface UseFormOptions<T> {
initialValues: T;
onSubmit: (values: T) => void | Promise<void>;
validate?: (values: T) => Partial<Record<keyof T, string>>;
}
function useForm<T extends Record<string, any>>({
initialValues,
onSubmit,
validate
}: UseFormOptions<T>) {
const [state, setState] = useState<FormState<T>>({
values: initialValues,
errors: {},
touched: {}
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setState(prev => ({
...prev,
values: { ...prev.values, [name]: value }
}));
};
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
const { name } = e.target;
setState(prev => ({
...prev,
touched: { ...prev.touched, [name]: true }
}));
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (validate) {
const errors = validate(state.values);
setState(prev => ({ ...prev, errors }));
if (Object.keys(errors).length > 0) {
return;
}
}
await onSubmit(state.values);
};
return {
...state,
handleChange,
handleBlur,
handleSubmit,
reset: () => setState({ values: initialValues, errors: {}, touched: {} })
};
}
// 使用
interface LoginForm {
email: string;
password: string;
}
function LoginPage() {
const form = useForm<LoginForm>({
initialValues: { email: '', password: '' },
validate: (values) => {
const errors: Partial<Record<keyof LoginForm, string>> = {};
if (!values.email) errors.email = 'Email required';
if (!values.password) errors.password = 'Password required';
return errors;
},
onSubmit: async (values) => {
console.log('Submit:', values);
}
});
return (
<form onSubmit={form.handleSubmit}>
<input
name="email"
value={form.values.email}
onChange={form.handleChange}
/>
{form.errors.email && <span>{form.errors.email}</span>}
</form>
);
}7.2 useFetch Hook
typescript
interface UseFetchOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers?: Record<string, string>;
body?: any;
}
interface UseFetchState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
function useFetch<T = any>(
url: string,
options?: UseFetchOptions
): UseFetchState<T> & { refetch: () => Promise<void> } {
const [state, setState] = useState<UseFetchState<T>>({
data: null,
loading: true,
error: null
});
const fetchData = useCallback(async () => {
try {
setState(prev => ({ ...prev, loading: true }));
const response = await fetch(url, {
method: options?.method || 'GET',
headers: options?.headers,
body: options?.body ? JSON.stringify(options.body) : undefined
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = (await response.json()) as T;
setState({ data, loading: false, error: null });
} catch (error) {
setState({
data: null,
loading: false,
error: error instanceof Error ? error : new Error(String(error))
});
}
}, [url, options]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { ...state, refetch: fetchData };
}
// 使用
interface User {
id: number;
name: string;
email: string;
}
function UserList() {
const { data: users, loading, error, refetch } = useFetch<User[]>('/api/users');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<ul>
{users?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<button onClick={refetch}>Refresh</button>
</div>
);
}