Может ли машинопись иметь возможность понять, является ли массив непустым, и, соответственно, определить типы некоторых методов массива? - PullRequest
1 голос
/ 03 мая 2019

Я недавно обновил машинопись и lodash, и теперь компилятор понимает это, например. методы типа _.last() будут возвращать значение, если массив не пустой и неопределенный, если он пустой. Это ожидаемое поведение как таковое, особенно если мы не знаем, является ли массив пустым или нет.

Однако в таких случаях, как вызов _.last([1,2,3]), мы просто знаем , что массив не пустой, и это всегда будет возвращать число. Более того, у меня есть несколько мест в коде, где я проверил отсутствие пустот и действую соответственно, например:

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

... где, просто взглянув на этот код, мы знаем, что оператор вернет элемент с типом элемента в этом массиве. Но тип возврата, тем не менее, T | undefined.

Я знаю, что могу просто привести значение в таких случаях. Но я бы предпочел не кастовать вещи.

Итак, мой вопрос: может ли машинопись понимать такие ситуации?

1 Ответ

1 голос
/ 03 мая 2019

Мы можем создать систему, в которой 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
...