interface Body {
size: number;
meta?: Meta;
}
export interface Request {
body?: Body;
}
我认为这会起作用:
type MyMeta = Request["body"]["meta"];
然而,因为body
是可选的,所以Request["body"]
的类型是Body | undefined
,所以我得到了
Property 'meta' does not exist on type 'Body | undefined'.ts(2339)
除了之外,还有其他方法吗
type MyMeta = Exclude<Request["body"], undefined>["meta"];
type MyMeta = Required<Request>['body']['meta'];
对于更深层的房产来说,这将是个问题。
使用DeepRequired
泛型也是个坏主意,现在meta
中的每个属性都是必需的:
export type DeepRequired<T> = Required<{
[K in keyof T]: DeepRequired<T[K]>
}>
type MyMeta = DeepRequired<Request>['body']['meta'];
您可以创建一个递归类型Index
,该类型接受一个键数组并遍历对象类型,同时放弃undefined
备选方案:
type Index<P, T> =
P extends readonly [infer Key, ...infer Rest]
? T extends undefined ? never : Index<Rest, T[Extract<Key, keyof T>]>
: T
因为Index
不约束路径类型,所以它只会为不正确的路径生成never
。这可以通过声明一个Shape
类型来弥补,该类型使用每个路径键的可选键(从我对另一个问题的回答中窃取https://stackoverflow.com/a/67351133/5770132,请参阅该答案以了解更多详细信息(。
type Shape<P> =
P extends readonly [infer Key, ...infer Rest]
? {[K in Extract<Key, PropertyKey>]?: Shape<Rest>}
: unknown
使用Shape
,我们可以声明一个在不正确的路径上给出错误的DeepIndex
:
type DeepIndex<T extends Shape<Path>, Path extends readonly PropertyKey[]> = Index<Path, T>
几个例子(添加了Meta
的定义(:
interface Meta { x: 42, meta: Meta }
interface Body { size: number; meta?: Meta }
export interface Request { body?: Body }
{ type Test = DeepIndex<Request, ['body']> }
// type Test = Body | undefined
{ type Test = DeepIndex<Request, ['body', 'size']> }
// type Test = number
{ type Test = DeepIndex<Request, ['body', 'meta', 'x']> }
// type Test = 42
{ type Test = DeepIndex<Request, ['body', 'meta', 'meta', 'meta', 'x']> }
// type Test = 42
{ type Test = DeepIndex<Request, ['nobody', 'size']> }
// ERROR: Type 'Request' has no properties in common with type
// '{ nobody?: { size?: unknown; } | undefined; }
请注意,如果最后一个属性是可选的,则结果包括undefined
,但如果前面的任何属性都是可选的则不会自动这样做。在这条道路上传播选择性是可能的,但会使事情变得相当复杂。从结果中删除undefined
也是可能的,但这将为碰巧具有undefined
类型的属性返回never
(但可能并不常见(。
TypeScript游乐场
您可以使用内置的NonNullable
类型,它只是Exclude<T, undefined | null>
:的别名
interface Body {
size: number;
meta?: { __notSpecified: never };
}
export interface Request {
body?: Body;
}
type MyMeta = NonNullable<Request["body"]>["meta"]; // { __notSpecified: never; } | undefined
TypeScript游乐场链接
除此之外,你真的没有别的办法了