首页 > 解决方案 > 循环嵌套数组时如何正确使用类型保护?

问题描述

在遍历数组时,我需要区分其元素的两种可能类型:

function _addDataAtIndex(
    totalData: Array<string | string[]>,
    dataToAdd: Array<Array<string | string[]>>,
    addIndex: number,
) {
    dataToAdd.forEach((dataArr) => {
        if (dataArr.every((x) => typeof x === 'string')) {
            // each value is a string - insert the entire array
            totalData.splice(addIndex, 0, dataArr);
        } else {
            totalData.splice(addIndex, 0, dataArr[0]);
        }
    });
}

然而,TypeScript 似乎无法推断嵌套数组的类型,即使看起来很健壮的类型保护也是如此。我仍然需要类型保护来确保我在正确的分支上,但是我必须转换数组,dataArr as string[]来告诉 TypeScript 没有问题。这是多余且脆弱的,我觉得必须有一种方法可以使用更清洁的防护装置来做到这一点。

我已经搜索了许多其他问题,但是我在那里找到的答案(例如自定义类型保护函数)也不起作用,请参阅这个游乐场

string | string[]有没有一种干净的方法可以在不改变整个结构的情况下进行区分?我所能想到的就是将内部值转换为一些自定义联合接口的对象,这对于这个用例来说太混乱了。

标签: arraysstringtypescriptnestedtypeguards

解决方案


TypeScript 无法理解您.every作为类型保护的调用。您可以为此编写自己的类型保护/谓词,尽管这不是必需的。类型 for.every有两个重载,一个是实际谓词:

    /**
     * Determines whether all the members of an array satisfy the specified test.
     * @param predicate A function that accepts up to three arguments. The every method calls
     * the predicate function for each element in the array until the predicate returns a value
     * which is coercible to the Boolean value false, or until the end of the array.
     * @param thisArg An object to which the this keyword can refer in the predicate function.
     * If thisArg is omitted, undefined is used as the this value.
     */
    every<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): this is S[];

    /**
     * Determines whether all the members of an array satisfy the specified test.
     * @param predicate A function that accepts up to three arguments. The every method calls
     * the predicate function for each element in the array until the predicate returns a value
     * which is coercible to the Boolean value false, or until the end of the array.
     * @param thisArg An object to which the this keyword can refer in the predicate function.
     * If thisArg is omitted, undefined is used as the this value.
     */
    every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;

要使用类型保护重载版本,您传递给的谓词.every本身必须是类型保护。您可以简单地向数组函数添加一个返回类型,将其标记为类型保护:

function _addDataAtIndex(
    totalData: Array<string | string[]>,
    dataToAdd: Array<Array<string | string[]>>,
    addIndex: number,
) {
    dataToAdd.forEach((dataArr) => {
        // Note the `: x is string` here
        if (dataArr.every((x): x is string => typeof x === 'string')) {
            // each value is a string - insert the entire array
            totalData.splice(addIndex, 0, dataArr);
        } else {
            totalData.splice(addIndex, 0, dataArr[0]);
        }
    });
}

现在它使用类型保护版本,.every它使 TypeScript 意识到dataArr它是string[]if 语句的那个​​分支中的一个。


推荐阅读