网站搜索

TypeScript 能否判断一个数组是否非空,并相应地推断某些数组方法的类型?


我最近更新了 TypeScript 和 Lodash,现在编译器能够理解,例如方法 _.last() 如果数组非空会返回一个值,如果数组为空则返回 undefined。这是预期的行为,尤其是当我们不知道数组是否为空时。

然而,在诸如调用 _.last([1,2,3]) 的情况下,我们只是知道数组非空,并且这总会返回一个数字。此外,我在代码中有几个地方检查了非空情况,并相应地采取了行动,比如:

if (!_.isEmpty(array)) {
  return _.last(array);
}

... 仅仅通过查看那段代码,我们就知道该语句将返回该数组中某个元素类型的元素。但返回类型仍然是 T | undefined。

我知道在这种情况下我可以直接转换值。但我宁愿避免转换东西。

所以我的问题是:TypeScript 有可能理解像这样的情况吗?

答案#1

我们可以创建一个系统,其中 isEmpty 会向数组的类型添加信息。这可以是一个我们称之为 HasElements<T> 的类型,其中 T 可以是 true 或 false。如果类型尚未经过测试,它的类型中应该包含两种可能性(T[] & (HasElements<true> | HasElements<false>))。不幸的是,这必须手动添加到类型中。

import _ from 'lodash'

declare module 'lodash' {
    type HasElements<T extends boolean> = T extends boolean ? { // distributive conditional, makes HasElements<boolean> == HasElements<true> | HasElements<false>
        "gurad-traits"?: {
            hasElements?: T
        }
    }: never
    interface LoDashStatic {
        isEmpty<T extends HasElements<boolean>>(value?: T): value is T & HasElements<false>;
        last<T> (array: List<T> & HasElements<true>): T;
        last<T> (array: (List<T> & HasElements<false> )| null | undefined): undefined;
}
}

function test<T>(array: T[] & _.HasElements<boolean>, defaultValue: T): T {
    if (!_.isEmpty(array)) {
        return _.last(array);
    }else {
        let u: T = _.last(array); /// err, returns undefined
        return defaultValue;
    }
}

test([1,2,3], 1); //HasElements does not influence the assignability of arrays