MST

星途 面试题库

面试题:复杂场景下TypeScript类型收缩的优化与陷阱

在一个大型的TypeScript项目中,有一个函数`processData`,它接收一个联合类型`DataUnion = { type: 'A', value: string } | { type: 'B', value: number[] } | { type: 'C', value: boolean }`作为参数。该函数需要根据`type`字段进行类型收缩,并对不同类型的数据进行复杂的处理逻辑(例如:对于`type === 'A'`,将字符串反转;对于`type === 'B'`,计算数组元素的平均值;对于`type === 'C'`,取布尔值的反值)。请实现这个`processData`函数,并分析在这种复杂场景下,可能出现的类型收缩相关的陷阱以及如何优化代码以避免这些陷阱。
31.2万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试
function processData(data: DataUnion): string | number | boolean {
    if (data.type === 'A') {
        return data.value.split('').reverse().join('');
    } else if (data.type === 'B') {
        return data.value.reduce((acc, cur) => acc + cur, 0) / data.value.length;
    } else if (data.type === 'C') {
        return!data.value;
    }
    // 防止遗漏分支,这里也可以抛出异常等
    return null as any; 
}

可能出现的类型收缩相关的陷阱

  1. 遗漏分支:如果新增了DataUnion的联合类型分支,但processData函数没有相应更新处理逻辑,就会出现处理逻辑不完整的情况。例如新增{ type: 'D', value: { prop: string } },函数就没有处理type === 'D'的情况。
  2. 类型判断错误:在类型收缩时,判断条件写错可能导致类型没有正确收缩。例如写成if (data.type === 'a')(注意这里是小写a),就无法正确处理type === 'A'的情况。

优化代码以避免这些陷阱的方法

  1. 使用never类型检查遗漏分支:可以在函数最后添加一个default分支,让TypeScript检查是否有遗漏的联合类型分支。例如:
function processData(data: DataUnion): string | number | boolean {
    switch (data.type) {
        case 'A':
            return data.value.split('').reverse().join('');
        case 'B':
            return data.value.reduce((acc, cur) => acc + cur, 0) / data.value.length;
        case 'C':
            return!data.value;
        default:
            const _exhaustiveCheck: never = data;
            return _exhaustiveCheck; 
    }
}

如果新增了DataUnion的联合类型分支,TypeScript会提示错误,因为default分支应该永远不会执行。 2. 仔细检查类型判断条件:在编写类型判断条件时,确保与联合类型中的type值严格匹配,避免拼写错误。可以使用常量来定义type的值,以减少出错概率。例如:

const TYPE_A = 'A';
const TYPE_B = 'B';
const TYPE_C = 'C';

function processData(data: DataUnion): string | number | boolean {
    if (data.type === TYPE_A) {
        return data.value.split('').reverse().join('');
    } else if (data.type === TYPE_B) {
        return data.value.reduce((acc, cur) => acc + cur, 0) / data.value.length;
    } else if (data.type === TYPE_C) {
        return!data.value;
    }
    return null as any; 
}