TypeScript 进阶指南(六):实战中的高级类型模式
1. 类型安全的状态管理(Redux Toolkit)
type CounterAction =
| { type: 'INCREMENT'; payload: number }
| { type: 'DECREMENT'; payload: number }
| { type: 'RESET' };
const counterReducer = (
state: number = 0,
action: CounterAction
): number => {
switch (action.type) {
case 'INCREMENT':
return state + action.payload;
case 'DECREMENT':
return state - action.payload;
case 'RESET':
return 0;
default:
return state;
}
};
const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
const useAppDispatch = () => useDispatch<AppDispatch>();
const CounterComponent = () => {
const count = useAppSelector(state => state.counter);
const dispatch = useAppDispatch();
return (
<div>
<button onClick={() => dispatch({ type: 'INCREMENT', payload: 5 })}>
+5
</button>
<span>{count}</span>
</div>
);
};
2. 高级类型工具实战
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface UserProfile {
id: number;
details: {
name: string;
address?: {
city: string;
street: string;
};
};
}
const partialProfile: DeepPartial<UserProfile> = {
details: {
address: { city: 'Beijing' }
}
};
type Entries<T> = {
[K in keyof T]: [K, T[K]];
}[keyof T][];
function typedEntries<T extends object>(obj: T): Entries<T> {
return Object.entries(obj) as Entries<T>;
}
const config = { apiUrl: '/data', timeout: 5000 };
typedEntries(config).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
3. 复杂表单类型处理
type FieldType = 'text' | 'number' | 'date';
type FormField<T extends FieldType> = {
type: T;
label: string;
required?: boolean;
config: T extends 'number'
? { min?: number; max?: number }
: T extends 'date'
? { minDate?: string; maxDate?: string }
: {};
};
const formConfig: FormField<FieldType>[] = [
{
type: 'text',
label: '用户名',
required: true,
config: {}
},
{
type: 'number',
label: '年龄',
config: { min: 0, max: 120 }
}
];
type FormData<T extends FormField<FieldType>[]> = {
[K in T[number]['label']]:
T[number] extends { type: 'number' } ? number : string;
};
type MyFormData = FormData<typeof formConfig>;
const formData: MyFormData = {
'用户名': 'Alice',
'年龄': 25
};
4. 类型安全的 HTTP 客户端封装
type ApiConfig<TPayload, TResponse> = {
url: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
data?: TPayload;
headers?: Record<string, string>;
validateResponse?: (res: unknown) => TResponse;
};
async function request<TPayload, TResponse>(
config: ApiConfig<TPayload, TResponse>
): Promise<TResponse> {
try {
const response = await fetch(config.url, {
method: config.method,
headers: config.headers,
body: config.data ? JSON.stringify(config.data) : undefined
});
const jsonData = await response.json();
if (config.validateResponse) {
return config.validateResponse(jsonData);
}
return jsonData as TResponse;
} catch (error) {
throw new Error(`Request failed: ${error}`);
}
}
interface UserData {
id: number;
name: string;
}
const fetchUser = async (userId: number) =>
request<never, UserData>({
url: `/users/${userId}`,
method: 'GET',
validateResponse: (res) => {
if (typeof res.id === 'number' && res.name) {
return res as UserData;
}
throw new Error('Invalid user data');
}
});
5. 类型守卫高级模式
function isStringArray(arr: unknown[]): arr is string[] {
return arr.every(item => typeof item === 'string');
}
type ApiResponse =
| { status: 'success'; unknown }
| { status: 'error'; message: string };
function handleResponse(response: ApiResponse) {
if (response.status === 'success') {
console.log('Data:', response.data);
} else {
console.error('Error:', response.message);
}
}
type Animal =
| { type: 'dog'; barkVolume: number }
| { type: 'cat'; purrDecibel: number };
function getAnimalSound(animal: Animal) {
switch (animal.type) {
case 'dog':
return `Bark at ${animal.barkVolume}dB`;
case 'cat':
return `Purr at ${animal.purrDecibel}dB`;
}
}
代码注释设计原则
- 解释为什么(Why):
type DeepPartial<T> = { ... };
- 强调类型安全点:
function request<TPayload, TResponse>(...)
- 指出潜在陷阱:
return jsonData as TResponse;
- 关联业务场景:
config: { min: 0, max: 120 }