[英]Optional chaining for typescript types
I have a data structure that is deeply nested and I want to be able to reference an inner type in it, but that type doesn't have its own name/definition.我有一个深度嵌套的数据结构,我希望能够在其中引用内部类型,但该类型没有自己的名称/定义。 For example:
例如:
MyQuery['system']['errors']['list'][number]
I auto-generate the MyQuery
type from a graphql query using graphql-codegen.我使用 graphql-codegen 从 graphql 查询自动生成
MyQuery
类型。 I want the type of a single error
, but there are two problems:我想要单个
error
的类型,但是有两个问题:
I tried the following:我尝试了以下方法:
type Error = NonNullable<NonNullable<NonNullable<MyQuery>['system']>['errors']>['list'][number]
?.['field']
also doesn't work)?.['field']
也不起作用)type Error = MyQuery?['system']?['errors']?['list']?[number]
const error = queryResult?.system?.errors?.list?.[0]
type Error: typeof error
import { DeepNonNullable } from 'utility-types'
type Error = DeepNonNullable<MyQuery>['system']['errors']['list'][number]
Basically what I am asking is if there is an easier way to do "optional chaining for types" in typescript.基本上我要问的是在 typescript 中是否有更简单的方法来执行“类型的可选链接”。 My API is very null-prone and it would be very useful if I could do this more easily than using several
NonNullable<T>
我的 API 非常容易出现空值,如果我可以比使用几个
NonNullable<T>
更容易地做到这一点,那将非常有用
if there is an easier way to do "optional chaining for types"
如果有更简单的方法来做“类型的可选链接”
No, unfortunately, as of yet there is no native way to "optionally chain" deeply nested types.不,不幸的是,到目前为止还没有“可选地链接”深度嵌套类型的本地方法。 There is, however, quite a roundabout way of emulating that with a complex recursive conditional generic type and paths.
然而,使用复杂的递归条件泛型类型和路径来模拟它是一种相当迂回的方法。 First, you would need a reusable helper for handling index signatures:
首先,您需要一个可重用的助手来处理索引签名:
type _IndexAccess<T, U extends keyof T, V extends string> = V extends "number"
? Exclude<T[U], undefined> extends { [x:number]: any } ?
Exclude<T[U], undefined>[number]
: undefined
: V extends "string" ?
Exclude<T[U], undefined> extends { [x:string]: any } ?
Exclude<T[U], undefined>[string]
: undefined
: V extends "symbol" ?
Exclude<T[U], undefined> extends { [x:symbol]: any } ?
Exclude<T[U], undefined>[symbol]
: undefined
: undefined;
Then you can create a helper type for recursively traversing the nested type relying on infer
and template literal types to process the path:然后,您可以创建一个辅助类型,用于递归遍历依赖于
infer
和模板文字类型的嵌套类型来处理路径:
type DeepAccess<T, K extends string> = K extends keyof T
? T[K]
: K extends `${infer A}.${infer B}`
? A extends keyof T
? DeepAccess<Exclude<T[A], undefined>, B>
: A extends `${infer C}[${infer D}]`
? DeepAccess<_IndexAccess<T, C extends keyof T ? C : never, D>, B>
: undefined
: K extends `${infer A}[${infer B}]`
? A extends keyof T
? B extends keyof T[A]
? T[A][B]
: _IndexAccess<T, A, B>
: undefined
: undefined;
It's not pretty, but it allows for elegantly lensing into nested types:它并不漂亮,但它允许优雅地映射到嵌套类型:
type MyQuery = {
system?: {
errors?: {
list?: [{
answer: 42,
questions: { known: false }[]
}]
}
}
};
// false
type t1 = DeepAccess<MyQuery, "system.errors.list[number].questions[number].known">;
// [{ answer: 42; questions: { known: false; }[]; }] | undefined
type t2 = DeepAccess<MyQuery, "system.errors.list">;
// 42
type t3 = DeepAccess<MyQuery, "system.errors.list[number].answer">;
// undefined
type t4 = DeepAccess<MyQuery, "system.errors.list.unknown">;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.