面试题答案
一键面试利用 TypeScript 复合类型对异步操作返回值、Redux action 和 state 进行精准类型定义
- 异步操作返回值类型定义
- 使用
Promise
类型:如果异步操作返回一个Promise
,可以使用Promise
泛型来定义其返回值类型。例如,假设异步操作是从后端获取用户信息,返回的是一个包含用户数据的对象。
interface User { id: number; name: string; email: string; } const fetchUser = (): Promise<User> => { // 实际的异步请求代码,这里省略 return Promise.resolve({id: 1, name: 'John', email: 'john@example.com'}); };
- 使用
async/await
语法时:async
函数默认返回一个Promise
,其返回值类型遵循同样的规则。
async function getUser(): Promise<User> { const response = await fetch('/api/user'); const data = await response.json(); return data; }
- 使用
- Redux action 类型定义
- 定义 action 类型常量:首先定义 action 类型的字符串常量,使用
const
声明,确保其不可变。
const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'; const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE';
- 定义 action 对象类型:使用
interface
或type
定义 action 对象的结构。对于有负载(payload)的 action,在类型中明确负载的类型。
interface FetchUserSuccessAction { type: typeof FETCH_USER_SUCCESS; payload: User; } interface FetchUserFailureAction { type: typeof FETCH_USER_FAILURE; payload: string; } type UserAction = FetchUserSuccessAction | FetchUserFailureAction;
- 创建 action 创建函数:根据定义的 action 类型,创建 action 创建函数,确保返回值类型正确。
const fetchUserSuccess = (user: User): FetchUserSuccessAction => ({ type: FETCH_USER_SUCCESS, payload: user }); const fetchUserFailure = (error: string): FetchUserFailureAction => ({ type: FETCH_USER_FAILURE, payload: error });
- 定义 action 类型常量:首先定义 action 类型的字符串常量,使用
- Redux state 类型定义
- 使用
interface
或type
定义 state 结构:根据项目中实际的状态数据结构进行定义。例如,对于用户相关的状态,可能包含用户信息、加载状态和错误信息。
interface UserState { user: User | null; loading: boolean; error: string | null; } const initialUserState: UserState = { user: null, loading: false, error: null };
- 在 reducer 中使用 state 类型:确保 reducer 函数的
state
参数和返回值类型都与定义的UserState
类型一致。
const userReducer = (state = initialUserState, action: UserAction): UserState => { switch (action.type) { case FETCH_USER_SUCCESS: return { ...state, user: action.payload, loading: false, error: null }; case FETCH_USER_FAILURE: return { ...state, user: null, loading: false, error: action.payload }; default: return state; } };
- 使用
面对频繁变动的后端 API 时,通过 TypeScript 类型系统进行高效前端代码调整与优化
- 使用接口(interface)或类型别名(type alias)抽象 API 响应数据类型:将 API 响应数据结构定义为
interface
或type
,这样在 API 变动时,只需要修改这些类型定义,而不需要在整个项目中查找和修改使用该数据的地方。例如:// 原来的 API 响应类型 interface OldUserResponse { user_id: number; user_name: string; email: string; } // API 变动后的响应类型 interface NewUserResponse { id: number; name: string; email: string; }
- 在 API 调用处使用类型:在调用 API 的函数中使用这些类型定义,确保返回值类型正确。
async function fetchUser(): Promise<NewUserResponse> { const response = await fetch('/api/user'); const data = await response.json(); return data; }
- 利用类型映射(type mapping)和条件类型(conditional types):当 API 变动涉及到字段的增删改时,可以使用类型映射和条件类型来灵活调整类型。例如,如果 API 新增了一个字段
phone
,可以这样定义:interface BaseUser { id: number; name: string; email: string; } // 新增 phone 字段后的类型 interface ExtendedUser extends BaseUser { phone: string; } type UserResponse = 'phone' extends keyof ExtendedUser? ExtendedUser : BaseUser;
- 使用工具类型(utility types):TypeScript 提供了很多实用的工具类型,如
Pick
、Omit
、Partial
等。当 API 变动时,可以利用这些工具类型快速调整代码中的类型。例如,如果 API 移除了email
字段,可以使用Omit
类型:interface User { id: number; name: string; email: string; } // 移除 email 字段后的类型 type NewUser = Omit<User, 'email'>;
- 自动化类型生成:可以使用工具如
openapi - generator
等,根据后端提供的 OpenAPI 规范自动生成 TypeScript 类型定义。这样当后端 API 变动时,重新生成类型定义并更新到项目中,能大大减少手动调整类型的工作量。