嵌套对象类型解构
Nest object type destructuring
如何从嵌套对象中获取类型定义?
在这个例子中,我得到了一个实体(产品)列表,我如何访问实体(节点)的类型定义。
这是使用relay-compiler
编译的
export type ProductAutoSuggestQueryResponse = {
node: {
products?: {
edges: ReadonlyArray<{
node: {
name: string;
id: string;
currencies: ReadonlyArray<string> | null;
};
} | null> | null;
} | undefined;
} | null;
};
我尝试过使用 Pick<>
,但它似乎变得非常复杂,数组导致我出现问题。
从概念上讲,您想深入了解 ProductAutoSuggestQueryResponse
和 index into it。让我们假设这种类型是:
type ProductAutoSuggestQueryResponse = {
node: {
products: {
edges: readonly {
node: {
name: string;
id: string;
currencies: readonly string[];
};
}[];
};
};
}
给定 ProductAutoSuggstQueryResponse
的修改版本的值 v
,您可以通过读取 v.node.products.edges[0].node
来获得所需节点类型的值。更一般地说,当 p
是 'node'
类型并且 q
是 'products'
和 r
类型时,您可以通过阅读 v[p][q][r][i][s]
来获得这样的值是 'edges'
类型,i
是 number
类型,s
是 'node'
类型。这种索引可以在类型级别使用索引访问类型完成:
type NodeType =
ProductAutoSuggestQueryResponse['node']['products']['edges'][number]['node'];
/* type NodeType = {
name: string;
id: string;
currencies: readonly string[];
} */
这就是你想要的。
太好了。不幸的是,您的 ProductAutoSuggestQueryResponse
类型与我上面写的不完全相同。通过对象结构的各个层级,直接有optional or otherwise null
able properties, meaning that they can include undefined
or null
. Instead of {a: string}
, you've got {a: string} | undefined
or {a: string} | null
or {a: string} | undefined | null
. You can't safely index into something which might be undefined
or null
, so the compiler won't let you use indexed access types
你可以做的是在索引之前使用the NonNullable<T>
utility type将{a: string} | undefined | null
之类的东西变成{a: string}
。这可以手动完成,但很烦人:
type NodeTypeManual = NonNullable<
NonNullable<
NonNullable<
NonNullable<
ProductAutoSuggestQueryResponse['node']
>['products']
>['edges']
>[number]
>['node']
/* type NodeTypeManual = {
name: string;
id: string;
currencies: ReadonlyArray<string> | null;
} */
如果你发现你要经常这样做,你可以写一个索引的递归conditional type that operates on a tuple:
type NonNullableDeepIndex<T, KT extends PropertyKey[]> =
KT extends [infer KF, ...infer KR] ? (
KF extends keyof NonNullable<T> ? (
NonNullableDeepIndex<NonNullable<T>[KF], Extract<KR, PropertyKey[]>>
) : never
) : NonNullable<T>;
类型函数NonNullableDeepIndex
接受一个类型T
和一个键元组KT
。如果键元组为空,我们只是 return NonNullable<T>
。否则,我们获取第一个键 KF
并使用 KF
索引到 NonNullable<T>
,然后获取新类型和键元组的其余部分 KR
,并递归计算 NonNullableDeepIndex
.
那么我们可以将NodeType
表达得少一些,如:
type NodeType = NonNullableDeepIndex<
ProductAutoSuggestQueryResponse,
["node", "products", "edges", number, "node"]
>;
/* type NodeType = {
name: string;
id: string;
currencies: ReadonlyArray<string> | null;
} */
如何从嵌套对象中获取类型定义?
在这个例子中,我得到了一个实体(产品)列表,我如何访问实体(节点)的类型定义。
这是使用relay-compiler
export type ProductAutoSuggestQueryResponse = {
node: {
products?: {
edges: ReadonlyArray<{
node: {
name: string;
id: string;
currencies: ReadonlyArray<string> | null;
};
} | null> | null;
} | undefined;
} | null;
};
我尝试过使用 Pick<>
,但它似乎变得非常复杂,数组导致我出现问题。
从概念上讲,您想深入了解 ProductAutoSuggestQueryResponse
和 index into it。让我们假设这种类型是:
type ProductAutoSuggestQueryResponse = {
node: {
products: {
edges: readonly {
node: {
name: string;
id: string;
currencies: readonly string[];
};
}[];
};
};
}
给定 ProductAutoSuggstQueryResponse
的修改版本的值 v
,您可以通过读取 v.node.products.edges[0].node
来获得所需节点类型的值。更一般地说,当 p
是 'node'
类型并且 q
是 'products'
和 r
类型时,您可以通过阅读 v[p][q][r][i][s]
来获得这样的值是 'edges'
类型,i
是 number
类型,s
是 'node'
类型。这种索引可以在类型级别使用索引访问类型完成:
type NodeType =
ProductAutoSuggestQueryResponse['node']['products']['edges'][number]['node'];
/* type NodeType = {
name: string;
id: string;
currencies: readonly string[];
} */
这就是你想要的。
太好了。不幸的是,您的 ProductAutoSuggestQueryResponse
类型与我上面写的不完全相同。通过对象结构的各个层级,直接有optional or otherwise null
able properties, meaning that they can include undefined
or null
. Instead of {a: string}
, you've got {a: string} | undefined
or {a: string} | null
or {a: string} | undefined | null
. You can't safely index into something which might be undefined
or null
, so the compiler won't let you use indexed access types
你可以做的是在索引之前使用the NonNullable<T>
utility type将{a: string} | undefined | null
之类的东西变成{a: string}
。这可以手动完成,但很烦人:
type NodeTypeManual = NonNullable<
NonNullable<
NonNullable<
NonNullable<
ProductAutoSuggestQueryResponse['node']
>['products']
>['edges']
>[number]
>['node']
/* type NodeTypeManual = {
name: string;
id: string;
currencies: ReadonlyArray<string> | null;
} */
如果你发现你要经常这样做,你可以写一个索引的递归conditional type that operates on a tuple:
type NonNullableDeepIndex<T, KT extends PropertyKey[]> =
KT extends [infer KF, ...infer KR] ? (
KF extends keyof NonNullable<T> ? (
NonNullableDeepIndex<NonNullable<T>[KF], Extract<KR, PropertyKey[]>>
) : never
) : NonNullable<T>;
类型函数NonNullableDeepIndex
接受一个类型T
和一个键元组KT
。如果键元组为空,我们只是 return NonNullable<T>
。否则,我们获取第一个键 KF
并使用 KF
索引到 NonNullable<T>
,然后获取新类型和键元组的其余部分 KR
,并递归计算 NonNullableDeepIndex
.
那么我们可以将NodeType
表达得少一些,如:
type NodeType = NonNullableDeepIndex<
ProductAutoSuggestQueryResponse,
["node", "products", "edges", number, "node"]
>;
/* type NodeType = {
name: string;
id: string;
currencies: ReadonlyArray<string> | null;
} */