我想创建一个类型,它允许访问对象的未定义属性,并为它们假设
any
的类型,并递归地执行此操作。让我用一个例子来说明这个问题。 (根据Darryl Noakes 的评论简化并更正了示例。)
const data = {a: "a", b: {c: 'hey'}}
type Data = typeof data & { [key: string]: any }
const dataOptional: Data = data
// This errors:) as expected, because the property "a" does not have such method
dataOptional.a.nonExistentStringMethod
// This two do not error:) as expected, because d has the correct type of "any"
dataOptional.d
dataOptional.d.nonExistentStringMethod
// This does error:( and that's the problem with this type
dataOptional.b.d
所以上述实现的问题是该语句不递归应用。重要的一点是,我事先有
data
的 JSON 值,然后我必须生成一些代码,最后,我可以导出原始 data
值,从而能够访问未定义的属性,如所述上面。
所以问题是,如何从 JSON 值获取到某些 TypeScript 代码,该代码以一种可以递归访问任何未定义属性的方式导出 JSON 值,并且访问的值将具有 any 的值,同时保持键入原始对象中定义的(嵌套或非嵌套)属性的数据。
您可以使用此实用程序类型:
type WithAny<T> = {
[key: string]: any;
} & {
[key in keyof T]: T[key] extends object ? WithAny<T[key]> : T[key]
}
它将以递归方式将给定类型中的任何对象类型与索引类型相交,以添加任何字符串键作为
any
。由于原始类型更具体(即更窄),因此这不会删除已知键的键入。
示例:
const data = {
a: "a",
b: {
c: "hey",
},
}
type AnyData = WithAny<typeof data>
const anyData: AnyData = data
anyData.a.foo // Error, expected
anyData.b.foo // No error, expected
/*
Pseudo-type of AnyData:
{
a: string
b: {
c: string
[key: string]: any
}
[key: string]: any
}
*/
警告
这会将交集应用于任何对象类型,其中包括
Date
、Map
等对象。如果您的数据包含这些对象,交集也会添加到它们中。extends
检查以排除它们。如果您不知道可能存在什么,请在使用此答案中给出的代码之前了解您在做什么。
处理示例
Date
:
type WithAny<T> = {
[key: string]: any
} & {
[key in keyof T]: T[key] extends object
? T[key] extends Date
? T[key]
: WithAny<T[key]>
: T[key]
}
您可以继续嵌套这些检查,将它们放置在嵌套
WithAny
所在的位置。