前言
TypeScript 已成为现代前端开发的标配,但在实际项目中,真正掌握类型系统的开发者却不多见。作为在多个企业级 Vue3/React 项目中深度实践 TypeScript 的工程师,我在 SaaS 平台、微前端架构、数据可视化系统等场景中,积累了丰富的类型设计经验。本文将从类型基础出发,深入探讨 TypeScript 的高级类型技巧,结合 Vue3 和 React 生态分享可落地的类型安全最佳实践。
一、TypeScript 类型系统核心概念
1.1 类型推断与类型注解
TypeScript 的类型推断能力可以大大减少我们的代码量:
// 类型推断示例
let message = 'Hello'; // 推断为 string
let count = 0; // 推断为 number
let isActive = true; // 推断为 boolean
let numbers = [1, 2, 3]; // 推断为 number[]
let user = { name: '张三', age: 30 }; // 推断为 { name: string; age: number }
// 显式类型注解
let greeting: string = 'Hello';
let total: number = 100;
let items: string[] = ['a', 'b', 'c'];
// 函数类型
function add(a: number, b: number): number {
return a + b;
}
// 箭头函数类型
const multiply = (a: number, b: number): number => a * b;
// 回调函数类型
function fetchData(callback: (data: string) => void) {
callback('data loaded');
}
1.2 联合类型与交叉类型
// 联合类型:多种可能性
type Status = 'pending' | 'success' | 'error';
interface SuccessResponse {
status: 'success';
data: User;
}
interface ErrorResponse {
status: 'error';
error: {
code: number;
message: string;
};
}
type ApiResponse = SuccessResponse | ErrorResponse;
// 类型守卫
function handleResponse(response: ApiResponse) {
if (response.status === 'success') {
// TypeScript 知道这是 SuccessResponse
console.log(response.data.name);
} else {
// TypeScript 知道这是 ErrorResponse
console.log(response.error.message);
}
}
// 交叉类型:组合多个类型
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
interface HasEmail {
email: string;
}
type Contact = HasName & HasAge & HasEmail;
const contact: Contact = {
name: '张三',
age: 30,
email: 'zhang@example.com'
};
1.3 泛型基础
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
const num = identity(42); // number
const str = identity('hello'); // string
// 泛型接口
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
interface User {
id: string;
name: string;
}
type UserResponse = ApiResponse<User[]>;
// 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: '1', name: '张三', age: 30 };
const name = getProperty(user, 'name'); // string
const age = getProperty(user, 'age'); // number
// getProperty(user, 'invalid'); // 编译错误
二、实用类型工具详解
2.1 内置工具类型
TypeScript 提供了一系列内置工具类型,我们可以灵活运用:
interface User {
id: string;
name: string;
email: string;
age: number;
password: string;
}
// Partial<T> - 将所有属性变为可选
type PartialUser = Partial<User>;
// Required<T> - 将所有属性变为必需
type RequiredUser = Required<PartialUser>;
// Pick<T, K> - 从 T 中选择部分属性
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit<T, K> - 从 T 中排除部分属性
type UserWithoutPassword = Omit<User, 'password'>;
// Record<K, V> - 创建键值对类型
type UserRole = 'admin' | 'editor' | 'viewer';
type Permission = 'read' | 'write' | 'delete';
const rolePermissions: Record<UserRole, Permission[]> = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read']
};
// Exclude<T, U> - 从 T 中排除可分配给 U 的类型
type Status = 'pending' | 'success' | 'error' | 'loading';
type NonErrorStatus = Exclude<Status, 'error'>; // 'pending' | 'success' | 'loading'
// Extract<T, U> - 从 T 中提取可分配给 U 的类型
type ErrorStatus = Extract<Status, 'error' | 'loading'>; // 'error' | 'loading'
// NonNullable<T> - 排除 null 和 undefined
type MaybeUser = User | null | undefined;
type DefinitelyUser = NonNullable<MaybeUser>; // User
// ReturnType<T> - 获取函数返回类型
function createUser() {
return { id: '1', name: '张三' };
}
type CreatedUser = ReturnType<typeof createUser>; // { id: string; name: string }
// Parameters<T> - 获取函数参数类型
function updateUser(id: string, data: Partial<User>) {}
type UpdateUserParams = Parameters<typeof updateUser>; // [id: string, data: Partial<User>]
2.2 高级类型技巧
// 条件类型
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<string[]>; // true
type B = IsArray<string>; // false
// 条件类型 + infer
type ArrayElement<T> = T extends (infer E)[] ? E : never;
type Element = ArrayElement<string[]>; // string
type Nested = ArrayElement<number[][]>; // number[]
// 获取 Promise 返回值类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type PromiseResult = Awaited<Promise<User>>; // User
// 递归类型 - 深只读
type DeepReadonly<T> = T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
type ReadonlyUser = DeepReadonly<User>;
// {
// readonly id: string;
// readonly name: string;
// readonly email: string;
// readonly age: number;
// readonly password: string;
// }
// 深度 Partial
type DeepPartial<T> = T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;
// 深度 Required
type DeepRequired<T> = T extends object
? { [K in keyof T]-?: DeepRequired<T[K]> }
: T;
// 任意深度键路径
type DeepKeys<T, P extends string = ''> = T extends object
? {
[K in keyof T & string]: T[K] extends object
? DeepKeys<T[K], `${P}${K}`> | `${P}${K}`
: `${P}${K}`
}[keyof T & string]
: never;
type UserKeys = DeepKeys<User>; // 'id' | 'name' | 'email' | 'age' | 'password'
type NestedKeys = DeepKeys<{ a: { b: { c: string } } }>; // 'a' | 'a.b' | 'a.b.c'
三、Vue3 组合式 API 类型实践
3.1 响应式数据类型定义
import { ref, reactive, computed, Ref } from 'vue';
// ref 类型推断
const count = ref(0); // Ref<number>
const name = ref('张三'); // Ref<string>
const user = ref({ name: '张三' }); // Ref<{ name: string }>
// 显式指定泛型
const countExplicit = ref<number>(0);
const data = ref<User | null>(null);
// reactive 类型
const state = reactive({
count: 0,
user: { name: '张三', age: 30 },
items: ['a', 'b', 'c']
}); // { count: number; user: { name: string; age: number }; items: string[] }
// 显式类型定义
interface FormState {
username: string;
password: string;
remember: boolean;
}
const form = reactive<FormState>({
username: '',
password: '',
remember: false
});
// computed 类型(自动推断)
const doubled = computed(() => count.value * 2); // ComputedRef<number>
const fullName = computed(() => `${user.value.name} - ${user.value.age}`); // ComputedRef<string>
// 带 get/set 的 computed
const message = computed({
get: () => `Hello, ${name.value}`,
set: (val: string) => { name.value = val.replace('Hello, ', ''); }
});
3.2 Props 与 Emits 类型定义
import { defineProps, defineEmits, PropType } from 'vue';
// Props 类型定义
interface User {
id: string;
name: string;
avatar?: string;
role: 'admin' | 'user' | 'guest';
}
interface Props {
// 基础类型
title: string;
count: number;
// 可选属性
description?: string;
// 联合类型
status: 'loading' | 'success' | 'error';
// 复杂类型
user: User;
// 数组类型
tags: string[];
// 函数类型
onClick: () => void;
onChange: (value: string) => void;
}
const props = defineProps<Props>();
// 带默认值的 Props
interface WithDefaults {
title: string;
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
}
const propsWithDefaults = withDefaults(defineProps<WithDefaults>(), {
size: 'medium',
disabled: false
});
// Emits 类型定义
interface Emits {
(e: 'update', value: string): void;
(e: 'delete', id: string): void;
(e: 'click', event: MouseEvent): void;
}
const emit = defineEmits<Emits>();
// 使用
emit('update', 'new value');
emit('delete', '123');
emit('click', new MouseEvent('click'));
3.3 组合式函数(Composables)类型设计
import { ref, computed, Ref, ComputedRef } from 'vue';
// 泛型组合式函数
export function useLocalStorage<T>(key: string, defaultValue: T) {
const value = ref<T>(defaultValue);
// 从 localStorage 读取
const stored = localStorage.getItem(key);
if (stored) {
try {
value.value = JSON.parse(stored);
} catch {
console.error(`Failed to parse localStorage key: ${key}`);
}
}
// 监听变化并保存
watch(value, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue));
}, { deep: true });
return value;
}
// 使用示例
const theme = useLocalStorage('theme', 'light'); // Ref<string>
const userPrefs = useLocalStorage('userPrefs', { sidebar: true }); // Ref<{ sidebar: boolean }>
// 带类型约束的组合式函数
interface PaginationOptions {
page: number;
pageSize: number;
total: number;
}
interface UsePaginationReturn<T> {
data: Ref<T[]>;
loading: Ref<boolean>;
pagination: ComputedRef<PaginationOptions>;
totalPages: ComputedRef<number>;
hasNextPage: ComputedRef<boolean>;
hasPrevPage: ComputedRef<boolean>;
nextPage: () => void;
prevPage: () => void;
goToPage: (page: number) => void;
}
export function usePagination<T>(
fetchFn: (page: number, pageSize: number) => Promise<T[]>
): UsePaginationReturn<T> {
const data = ref<T[]>([]) as Ref<T[]>;
const loading = ref(false);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const pagination = computed(() => ({
page: currentPage.value,
pageSize: pageSize.value,
total: total.value
}));
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const hasNextPage = computed(() => currentPage.value < totalPages.value);
const hasPrevPage = computed(() => currentPage.value > 1);
async function fetchData() {
loading.value = true;
try {
const result = await fetchFn(currentPage.value, pageSize.value);
data.value = result;
} finally {
loading.value = false;
}
}
function nextPage() {
if (hasNextPage.value) {
currentPage.value++;
fetchData();
}
}
function prevPage() {
if (hasPrevPage.value) {
currentPage.value--;
fetchData();
}
}
function goToPage(page: number) {
currentPage.value = Math.max(1, Math.min(page, totalPages.value));
fetchData();
}
return {
data,
loading,
pagination,
totalPages,
hasNextPage,
hasPrevPage,
nextPage,
prevPage,
goToPage
};
}
3.4 Vue Router 类型安全
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
// 路由元信息类型定义
interface Meta {
title: string;
requiresAuth?: boolean;
roles?: ('admin' | 'user')[];
keepAlive?: boolean;
}
// 全局类型声明
declare module 'vue-router' {
interface RouteMeta extends Meta {}
}
const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('@/layouts/MainLayout.vue'),
meta: { title: '首页', requiresAuth: false },
children: [
{
path: '',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { title: '仪表盘' }
},
{
path: 'users/:id',
name: 'UserDetail',
component: () => import('@/views/UserDetail.vue'),
meta: { title: '用户详情', requiresAuth: true, roles: ['admin'] },
props: true // route.params 作为 props
}
]
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
// 路由守卫类型安全
router.beforeEach((to, from, next) => {
// 类型安全的 meta 访问
const { requiresAuth, roles, title } = to.meta;
if (requiresAuth && !isAuthenticated()) {
next({ name: 'Login', query: { redirect: to.fullPath } });
return;
}
if (roles && !hasRole(roles)) {
next({ name: 'Forbidden' });
return;
}
document.title = `${title} - ${import.meta.env.VITE_APP_TITLE}`;
next();
});
// 类型安全的路由跳转
function navigateToUser(id: string) {
router.push({
name: 'UserDetail',
params: { id }, // number 会自动转为 string
query: { tab: 'profile' },
hash: '#settings'
});
}
四、React 类型最佳实践
4.1 组件 Props 类型设计
import React, { useState, useCallback, ReactNode, MouseEvent, ChangeEvent } from 'react';
// 基础 Props
interface ButtonProps {
children: ReactNode;
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
variant?: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
}
function Button({
children,
onClick,
variant = 'primary',
size = 'medium',
disabled = false
}: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
}
// 泛型组件
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => ReactNode;
keyExtractor: (item: T) => string;
emptyMessage?: string;
}
function List<T>({ items, renderItem, keyExtractor, emptyMessage = 'No items' }: ListProps<T>) {
if (items.length === 0) {
return <div className="empty">{emptyMessage}</div>;
}
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>
{renderItem(item, index)}
</li>
))}
</ul>
);
}
// 使用示例
interface User {
id: string;
name: string;
email: string;
}
const UserList: React.FC<{ users: User[] }> = ({ users }) => (
<List
items={users}
keyExtractor={(user) => user.id}
renderItem={(user) => (
<div>
<strong>{user.name}</strong>
<span>{user.email}</span>
</div>
)}
/>
);
// 受控组件 Props
interface InputProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
type?: 'text' | 'email' | 'password' | 'number';
error?: string;
disabled?: boolean;
}
function Input({
value,
onChange,
placeholder,
type = 'text',
error,
disabled
}: InputProps) {
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value);
};
return (
<div className={`input-wrapper ${error ? 'has-error' : ''}`}>
<input
type={type}
value={value}
onChange={handleChange}
placeholder={placeholder}
disabled={disabled}
/>
{error && <span className="error-message">{error}</span>}
</div>
);
}
4.2 Context 类型安全
import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
// 1. 定义 Context 类型
interface AuthState {
user: User | null;
isAuthenticated: boolean;
isLoading: boolean;
}
interface AuthContextValue extends AuthState {
login: (email: string, password: string) => Promise<void>;
logout: () => void;
updateUser: (data: Partial<User>) => void;
}
// 2. 创建 Context(带默认值)
const AuthContext = createContext<AuthContextValue | null>(null);
// 3. Provider 组件
interface AuthProviderProps {
children: ReactNode;
}
export function AuthProvider({ children }: AuthProviderProps) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(false);
const isAuthenticated = user !== null;
const login = useCallback(async (email: string, password: string) => {
setIsLoading(true);
try {
const response = await api.login({ email, password });
setUser(response.user);
} finally {
setIsLoading(false);
}
}, []);
const logout = useCallback(() => {
setUser(null);
api.logout();
}, []);
const updateUser = useCallback((data: Partial<User>) => {
setUser(prev => prev ? { ...prev, ...data } : null);
}, []);
const value: AuthContextValue = {
user,
isAuthenticated,
isLoading,
login,
logout,
updateUser
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
// 4. 自定义 Hook(带类型安全的访问)
export function useAuth(): AuthContextValue {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
// 使用示例
function ProfileButton() {
const { user, isAuthenticated, logout } = useAuth();
if (!isAuthenticated) {
return <Link to="/login">登录</Link>;
}
return (
<div>
<span>欢迎, {user!.name}</span>
<button onClick={logout}>退出</button>
</div>
);
}
4.3 Hooks 类型定义
import { useState, useEffect, useRef, useCallback, DependencyList } from 'react';
// useDebounce
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
}
// useAsync
interface AsyncState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
interface UseAsyncReturn<T> extends AsyncState<T> {
execute: () => Promise<void>;
reset: () => void;
}
export function useAsync<T>(
asyncFn: () => Promise<T>,
dependencies: DependencyList = []
): UseAsyncReturn<T> {
const [state, setState] = useState<AsyncState<T>>({
data: null,
loading: true,
error: null
});
useEffect(() => {
let mounted = true;
async function execute() {
setState({ data: null, loading: true, error: null });
try {
const result = await asyncFn();
if (mounted) {
setState({ data: result, loading: false, error: null });
}
} catch (error) {
if (mounted) {
setState({ data: null, loading: false, error: error as Error });
}
}
}
execute();
return () => {
mounted = false;
};
}, dependencies);
const reset = useCallback(() => {
setState({ data: null, loading: false, error: null });
}, []);
return { ...state, execute: asyncFn, reset };
}
// useClickOutside
export function useClickOutside<T extends HTMLElement>(
callback: () => void
): React.RefObject<T | null> {
const ref = useRef<T>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [callback]);
return ref;
}
五、工程实践:类型安全的 API 层
5.1 统一 API 响应类型
// types/api.ts
// API 响应基础类型
interface BaseResponse<T = unknown> {
code: number;
message: string;
}
interface SuccessResponse<T> extends BaseResponse {
code: 0 | 200;
data: T;
}
interface ErrorResponse extends BaseResponse {
code: number;
error?: {
code: string;
details?: Record<string, string[]>;
};
}
type ApiResult<T> = SuccessResponse<T> | ErrorResponse;
// 提取 data 类型
type ApiData<T> = T extends SuccessResponse<infer D> ? D : never;
// 分页类型
interface PaginationParams {
page: number;
pageSize: number;
}
interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}
// 请求配置
interface RequestConfig {
baseURL: string;
timeout: number;
headers?: Record<string, string>;
}
5.2 Axios 封装与类型推断
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import type { ApiResult, SuccessResponse } from './types/api';
// 创建 axios 实例
const api: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json'
}
});
// 请求拦截器
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// 响应拦截器
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// 处理未授权
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// 类型安全的请求方法
async function request<T>(
config: AxiosRequestConfig
): Promise<SuccessResponse<T>> {
const response: AxiosResponse<ApiResult<T>> = await api(config);
if (response.data.code !== 0 && response.data.code !== 200) {
throw new Error(response.data.message);
}
return response.data as SuccessResponse<T>;
}
// API 方法类型
type ApiMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
function createApiMethod<M extends ApiMethod>(method: M) {
return async <T, D = unknown>(
url: string,
data?: D,
config?: AxiosRequestConfig
): Promise<SuccessResponse<T>> => {
return request<T>({
method,
url,
...(method === 'get' ? { params: data } : { data }),
...config
});
};
}
// API 方法
export const get = createApiMethod('get');
export const post = createApiMethod('post');
export const put = createApiMethod('put');
export const patch = createApiMethod('patch');
export const del = createApiMethod('delete');
5.3 API 模块化组织
// api/user.ts
import { get, post } from './client';
import type { User, UserCreate, UserUpdate } from '@/types/models';
export const userApi = {
list: (params: { page: number; pageSize: number }) =>
get<PaginatedResponse<User>>('/users', params),
getById: (id: string) =>
get<User>(`/users/${id}`),
create: (data: UserCreate) =>
post<User>('/users', data),
update: (id: string, data: UserUpdate) =>
post<User>(`/users/${id}`, data),
delete: (id: string) =>
post<void>(`/users/${id}/delete`, {}),
updatePassword: (id: string, password: string) =>
post<void>(`/users/${id}/password`, { password })
};
// api/dashboard.ts
import { get } from './client';
import type { DashboardStats } from '@/types/models';
export const dashboardApi = {
getStats: () =>
get<DashboardStats>('/dashboard/stats'),
getTrend: (params: { period: 'day' | 'week' | 'month' }) =>
get<TrendData[]>('/dashboard/trend', params),
getRankings: () =>
get<RankingItem[]>('/dashboard/rankings')
};
// 使用示例
async function fetchUserList() {
try {
const response = await userApi.list({ page: 1, pageSize: 20 });
// response.data 自动类型化为 PaginatedResponse<User>
console.log(response.data.items);
console.log(response.data.total);
} catch (error) {
console.error('Failed to fetch users:', error);
}
}
六、类型测试与类型守卫
6.1 类型守卫函数
// 基础类型守卫
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function isNumber(value: unknown): value is number {
return typeof value === 'number';
}
function isArray(value: unknown): value is unknown[] {
return Array.isArray(value);
}
function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
// 自定义类型守卫
interface Dog {
kind: 'dog';
bark(): void;
}
interface Cat {
kind: 'cat';
meow(): void;
}
type Animal = Dog | Cat;
function isDog(animal: Animal): animal is Dog {
return animal.kind === 'dog';
}
function makeSound(animal: Animal) {
if (isDog(animal)) {
animal.bark();
} else {
animal.meow();
}
}
// 复杂的类型守卫
interface ApiErrorResponse {
status: 'error';
error: {
code: string;
message: string;
};
}
interface ApiSuccessResponse<T> {
status: 'success';
data: T;
}
type ApiResponse<T> = ApiSuccessResponse<T> | ApiErrorResponse;
function isSuccessResponse<T>(
response: ApiResponse<T>
): response is ApiSuccessResponse<T> {
return response.status === 'success';
}
function handleResponse<T>(response: ApiResponse<T>) {
if (isSuccessResponse(response)) {
// TypeScript 知道这是 ApiSuccessResponse<T>
console.log(response.data);
} else {
// TypeScript 知道这是 ApiErrorResponse
console.error(response.error.message);
}
}
6.2 穷举检查
// never 类型用于穷举检查
type Status = 'pending' | 'success' | 'error';
function handleStatus(status: Status) {
switch (status) {
case 'pending':
return '处理中...';
case 'success':
return '成功';
case 'error':
return '失败';
default:
// never 确保所有情况都被处理
const _exhaustive: never = status;
throw new Error(`Unknown status: ${_exhaustive}`);
}
}
// 联合类型穷举检查
type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost';
const buttonStyles: Record<ButtonVariant, string> = {
primary: 'btn-primary',
secondary: 'btn-secondary',
danger: 'btn-danger',
ghost: 'btn-ghost'
};
function getButtonClass(variant: ButtonVariant): string {
// 如果新增 variant 但忘记更新 buttonStyles,
// TypeScript 会报错
return buttonStyles[variant];
}
七、总结
TypeScript 的类型系统是一把双刃剑:使用得当可以让代码坚不可摧,使用不当反而会增加复杂度。通过本文的实践分享,我们可以得出以下关键结论:
类型设计原则:
-
类型优先 --- 优先设计类型,再实现逻辑
-
最小类型 --- 使用最具体的类型,不要过度泛化
-
类型即文档 --- 好的类型定义胜过千言万语
-
DRY 原则 --- 善用工具类型,避免重复定义
Vue3 类型实践:
-
ref<T>()/reactive<T>()泛型指定复杂类型 -
defineProps<T>()/defineEmits<T>()强类型 Props 和事件 -
组合式函数返回精确的
Ref/ComputedRef类型 -
Router 的
RouteMeta扩展实现类型安全的 meta
React 类型实践:
-
Props 接口清晰定义,泛型组件复用逻辑
-
Context 提供完整的类型约束
-
自定义 Hook 返回值类型精确化
-
API 层类型自动推断,减少手动类型标注
工程化建议:
-
开启
strict模式,逐步解决类型错误 -
使用
tsconfig.json的strict系列选项 -
配置 ESLint 的
@typescript-eslint规则 -
编写类型测试,确保关键类型的正确性
在实践中,TypeScript 的类型系统是一座可以无限探索的宝库。掌握这些技巧,不仅能提升代码质量,更能让我们在重构和扩展时游刃有余。
参考资源:
-
TypeScript 官方文档:https://www.typescriptlang.org/
-
TypeScript 工具类型手册:https://www.typescriptlang.org/docs/handbook/utility-types.html
-
React + TypeScript 指南:https://react.dev/learn/typescript
💡 提示:类型系统是渐进式的,建议从简单类型开始,逐步引入高级技巧,避免过度设计。