MST

星途 面试题库

面试题:在复杂的TypeScript项目中如何设计类型保护以确保类型安全

假设你正在开发一个大型的前端应用,其中有多种类型的用户对象(如普通用户、管理员用户、VIP用户等),并且不同类型用户有不同的操作权限。请描述你将如何设计类型保护机制,以便在处理用户相关操作时能够准确地缩小类型范围,确保类型安全,同时说明如何避免类型保护可能带来的代码冗余问题。
44.9万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

设计类型保护机制

  1. 基于属性检查
    • 在不同类型的用户对象中,定义一个能够区分用户类型的属性。例如,所有用户对象都有一个userType属性,值为'normal'(普通用户)、'admin'(管理员用户)、'vip'(VIP用户)等。
    • 在处理用户相关操作的函数中,通过检查这个属性来进行类型保护。比如:
    interface NormalUser {
        userType: 'normal';
        // 普通用户的其他属性和方法
        name: string;
        age: number;
    }
    interface AdminUser {
        userType: 'admin';
        // 管理员用户的其他属性和方法
        name: string;
        age: number;
        managePermissions: string[];
    }
    interface VIPUser {
        userType: 'vip';
        // VIP用户的其他属性和方法
        name: string;
        age: number;
        specialBenefits: string[];
    }
    type User = NormalUser | AdminUser | VIPUser;
    function handleUser(user: User) {
        if (user.userType === 'admin') {
            // 这里user的类型被缩小为AdminUser
            console.log(user.managePermissions);
        } else if (user.userType === 'vip') {
            // 这里user的类型被缩小为VIPUser
            console.log(user.specialBenefits);
        } else {
            // 这里user的类型被缩小为NormalUser
            console.log(user.name);
        }
    }
    
  2. 基于函数检查
    • 可以定义类型谓词函数。例如:
    function isAdminUser(user: User): user is AdminUser {
        return user.userType === 'admin';
    }
    function handleUserWithPredicate(user: User) {
        if (isAdminUser(user)) {
            // 这里user的类型被缩小为AdminUser
            console.log(user.managePermissions);
        } else {
            // user的类型为NormalUser | VIPUser
        }
    }
    

避免代码冗余

  1. 提取公共逻辑

    • 如果不同类型用户有一些共同的操作逻辑,将这些逻辑提取到一个公共函数中。例如,所有用户都有登录操作,这个操作的逻辑可以提取出来:
    function login(user: User) {
        console.log(`${user.name} logged in`);
    }
    
    • 然后在处理不同类型用户的函数中调用这个公共函数。
  2. 使用继承或混入

    • 如果使用面向对象编程,可以利用继承来复用代码。例如,创建一个基类UserBase,包含一些通用的属性和方法,然后NormalUserAdminUserVIPUser继承自这个基类。
    class UserBase {
        name: string;
        age: number;
        constructor(name: string, age: number) {
            this.name = name;
            this.age = age;
        }
        commonFunction() {
            console.log('This is a common function for all users');
        }
    }
    class NormalUser extends UserBase {
        userType: 'normal';
        constructor(name: string, age: number) {
            super(name, age);
            this.userType = 'normal';
        }
    }
    class AdminUser extends UserBase {
        userType: 'admin';
        managePermissions: string[];
        constructor(name: string, age: number, managePermissions: string[]) {
            super(name, age);
            this.userType = 'admin';
            this.managePermissions = managePermissions;
        }
    }
    
    • 另一种方式是使用混入(mixin),将一些功能模块混入到不同类型的用户对象中,以避免重复代码。
  3. 使用模板字面量类型和映射类型(在TypeScript中)

    • 例如,可以通过映射类型为不同类型用户生成相似的操作函数,减少重复代码。
    type UserType = 'normal' | 'admin' | 'vip';
    type UserPermissions = {
        normal: string[];
        admin: string[];
        vip: string[];
    };
    type UserOperation<T extends UserType> = {
        [K in T]: (user: { userType: K }) => void;
    };
    const userOperations: UserOperation<UserType> = {
        normal(user) {
            console.log('Normal user operation');
        },
        admin(user) {
            console.log('Admin user operation');
        },
        vip(user) {
            console.log('VIP user operation');
        }
    };
    function performUserOperation(user: User) {
        userOperations[user.userType](user);
    }