【问题标题】:Dealing with nested possibly null values in Typescript在 Typescript 中处理嵌套的可能为空的值
【发布时间】:2019-02-17 21:52:03
【问题描述】:

我正在使用 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 端点始终返回值之外,还有更好的检查方法吗?

【问题讨论】:

  • 你试过“npm install @types/lodash”吗? ts 编译器到底在抱怨什么?
  • 是的,包括在内。错误是value is possibly null
  • 快速查看lodash/has 的类型定义只是接受路径和对象并返回布尔值。这可能不足以让编译器理解它在做什么。
  • 如果您使用(data as any).getCoach.workouts.items.map(…),它会起作用,对吧?
  • 如果我禁用严格的空检查,它也会起作用。但我想在这里保持类型安全。如果值实际上是null,则执行(data as any).getCoach.workouts.items.map(…) 会在运行时爆炸

标签: reactjs typescript graphql lodash apollo


【解决方案1】:

TypeScript 的功能还不够强大,无法代表您正在尝试做的事情。理想情况下,JavaScript 和 TypeScript 将具有 null-coalescing operator 并且您会使用它。但它没有。

如果你尝试强输入lodashhas方法,你会发现编译器不能concatenate string literal types或者执行regular expression operations on them,所以编译器没有办法获取字符串'getCoach.workouts.items'并理解getCoach 将是data 的键,workouts 将是data.getCoach 的键,等等。

即使不使用has,要表示此处涉及的那种嵌套类型操作而不遇到recursion 导致编译器生气或崩溃的问题也是很棘手的。

我能做的最好的事情是:将对象包装在 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*/}) 
}

不知道实现是否完美;当我尝试时,它似乎有效。与您从 Stack Overflow 获得的任何东西一样,您的里程可能会有所不同,并且需要购买者注意。也许它会帮助你。祝你好运!

【讨论】:

  • 我会接受这个作为答案。我猜只需要等待功能被引入到 TS 中。会尝试你的解决方案,但这有点超出我的想象......
【解决方案2】:

编辑:

可选链接已released in typescript 3.7!

您现在可以这样做:

const items = data?.getCoach?.workouts?.items

items && items.map((v) => {
    console.log(v)
})

上一个答案:

只是想我会提到optional chaining 已进入 TC39 的第 2 阶段!

我认为目前没有任何巧妙的解决方案,所以像 jcalz 的回答这样的事情现在可能不得不做。

希望 TS 早点引入它,就像他们对 async/await 所做的一样 ?我可以更新这个答案。

【讨论】:

  • 空值合并是?? ?. 实际上称为可选链。
  • 好地方,刚刚更新
猜你喜欢
  • 2013-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-29
  • 1970-01-01
  • 2019-08-31
  • 2012-05-08
相关资源
最近更新 更多