Работа с вложенными возможно нулевыми значениями в Typescript - PullRequest
0 голосов
/ 13 сентября 2018

Я использую GraphQL и автоматически генерируемые типы для своих запросов с Typescript. Он имеет тенденцию создавать довольно много вложенных type | null типов. Поэтому мне нужно сделать довольно длинные проверки для ненулевых значений. Е.Г.

            data &&
            data.getCoach &&
            data.getCoach.workouts &&
            data.getCoach.workouts.items &&
            data.getCoach.workouts.items.map((workout) => {/* do something*/});

Я пытался использовать lodash has помощник для проверки существования пути, например

has('getCoach.workouts.items', data) &&
data.getCoach.workouts.items.map((workout) => {/* do something*/});

но компилятор ts, похоже, этого не понимает.

Есть ли лучший способ проверить это, кроме того, чтобы моя конечная точка GraphQL всегда возвращала значение?

Ответы [ 2 ]

0 голосов
/ 25 июня 2019

Просто подумал, что упомяну, что аннулирование слияния достигло стадии 2 на TC39!

Я не думаю, что в данный момент есть какие-то изящные решения, поэтому что-то вроде ответа jcalz, возможно, придется сделать пока.

Надеюсь, TS представит это рано, как они сделали с async / await ?, и я могу обновить этот ответ.

0 голосов
/ 13 сентября 2018

TypeScript не достаточно мощный, чтобы представлять то, что вы пытаетесь сделать.В идеале JavaScript и TypeScript должны иметь оператор слияния нуля , и вы бы это использовали.Но у него этого нет.

Если вы попытаетесь строго набрать метод lodash has, вы обнаружите, что компилятор не может объединить строковые литералы типов или выполнить операции с регулярными выражениями над ними , поэтому компилятор не может взять строку 'getCoach.workouts.items' и понять, что getCoach будет ключом data, а workouts будет ключом data.getCoach и т. д.

Даже без использования has сложно представить, что здесь происходит манипулирование вложенными типами, не сталкиваясь с проблемами с рекурсией , которые вызывают недовольство компилятораили сбой.

Лучшее, что я могу сделать: обернуть объект в Proxy (требуется ES2015 или новее), который всегда имеет значение для любой клавиши, которую вы хотите использовать, и вы используетеспециальное имя ключа (например, "value") для извлечения фактического значения свойства или undefined, если его нет.Вот реализация:

type WrapProperties<T, K extends keyof any> = Record<K, T> & { [P in keyof T]-?: WrapProperties<T[P], K> }

function wrapProperties<T>(val: T): WrapProperties<T, "value">;
function wrapProperties<T, K extends keyof any>(val: T, valueProp: K): WrapProperties<T, K>;
function wrapProperties(val: any, valueProp: keyof any = "value"): WrapProperties<any, any> {
    return new Proxy({}, {
        get: (_, p) => p === valueProp ? val : wrapProperties(
            (typeof val === 'undefined') || (val === null) ? val : val[p], valueProp
        )
    });
}

Итак, вместо этого:

if (data && data.getCoach && data.getCoach.workouts && data.getCoach.workouts.items) {
  data.getCoach.workouts.items.map((workout) => {/* do something*/}) 
}

Вы должны быть в состоянии сделать это:

const wrappedData = wrapProperties(data, "val");    
if (wrappedData.getCoach.workouts.items.value) {
  wrappedData.getCoach.workouts.items.value.map((workout) => {/* do something*/}) 
}

Я незнать, является ли реализация perfect ;кажется, работает, когда я пытаюсь это сделать.Как и все, что вы получаете от переполнения стека, ваш пробег может варьироваться и будьте бдительны.Может быть, это поможет вам.Удачи!

...