MST
星途 面试题库

面试题:TypeScript 复合类型在大型前端项目架构中的深度优化与实践

在一个大型的基于 React 和 TypeScript 的前端项目中,组件之间存在复杂的数据交互和状态管理。项目中使用了 Redux 进行状态管理,且存在多个不同类型的异步操作。请描述如何利用 TypeScript 的复合类型对异步操作的返回值、Redux action 和 state 进行精准的类型定义,以提高代码的可维护性和稳定性。同时,说明在面对频繁变动的后端 API 时,如何通过 TypeScript 的类型系统进行高效的前端代码调整与优化。
39.6万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

利用 TypeScript 复合类型对异步操作返回值、Redux action 和 state 进行精准类型定义

  1. 异步操作返回值类型定义
    • 使用 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;
    }
    
  2. Redux action 类型定义
    • 定义 action 类型常量:首先定义 action 类型的字符串常量,使用 const 声明,确保其不可变。
    const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS';
    const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE';
    
    • 定义 action 对象类型:使用 interfacetype 定义 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
    });
    
  3. Redux state 类型定义
    • 使用 interfacetype 定义 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 类型系统进行高效前端代码调整与优化

  1. 使用接口(interface)或类型别名(type alias)抽象 API 响应数据类型:将 API 响应数据结构定义为 interfacetype,这样在 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;
    }
    
  2. 利用类型映射(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;
    
  3. 使用工具类型(utility types):TypeScript 提供了很多实用的工具类型,如 PickOmitPartial 等。当 API 变动时,可以利用这些工具类型快速调整代码中的类型。例如,如果 API 移除了 email 字段,可以使用 Omit 类型:
    interface User {
        id: number;
        name: string;
        email: string;
    }
    // 移除 email 字段后的类型
    type NewUser = Omit<User, 'email'>;
    
  4. 自动化类型生成:可以使用工具如 openapi - generator 等,根据后端提供的 OpenAPI 规范自动生成 TypeScript 类型定义。这样当后端 API 变动时,重新生成类型定义并更新到项目中,能大大减少手动调整类型的工作量。