如何在具有可选属性的对象中递归地将属性类型从一个更改为另一个



情况:我通过JSON将MongoDB与REST API一起使用。MongoDB使用对象来代替文档的标识符,但当字符串化(例如作为响应体发送(时,它们会转换为字符串。

下面的例子显然被删减了很多,但可以用来重现我的问题。

// Should recursively convert all ObjectId types to string types
type JsonDocument<D> = {
[K in keyof D]: D[K] extends ObjectId ? string : D[K] extends object ? JsonDocument<D[K]> : D[K];
};
// The types for the document as it appears in the database
type FooDocument = {
_id: ObjectId;
updatedBy?: ObjectId;
};
// The types for the document as it appears in a response body
type FooJsonDocument = JsonDocument<FooDocument>;
// What I wanted FooJsonDocument to be
type ExpectedResult = {
_id: string;
updatedBy?: string;
};
// What FooJsonDocument actually is
type ActualResult = {
_id: string;
updatedBy?: ObjectId;
};

ObjectId类型的所有必需属性都按预期更改为string,但没有更改ObjectId类型的任何可选属性。

如何修改JsonDocument以正确修改可选属性?

我来自Typescript,递归地更改属性类型,但这个问题不询问可选属性,所以我觉得它不是重复的。尽管如此,它还是有用的。

问题是(ObjectID | undefined) extends ObjectID不是真的,因为undefined extends ObjectID不是真的。所以它取条件的负分支。

由于您不在乎这些值是可选的,因此可以在测试类型之前将undefined从类型中删除。这样ObjectID extends ObjectID为true,并且您采用正条件路径。

您可以通过使所有属性都是必需的Required<D>,或者在测试NonNullable<D[K]>时删除每个属性的可空性来实现这一点。

type JsonDocument<D> = {
[K in keyof D]:
NonNullable<D[K]> extends ObjectId ? string
: D[K] extends object ? JsonDocument<D[K]>
: D[K];
};

打字游戏场的工作示例

这是我用于JSON:的类型

type Unserializable = Function | RegExp | Error;
export type ToJSON<T> = T extends undefined
? ToJSON<T extends undefined ? never : T> | undefined
: T extends Unserializable
? never
: T extends { toJSON: () => any }
? ReturnType<T['toJSON']>
: T extends object
? {
[K in keyof T]: ToJSON<T[K]>;
}
: T extends (infer A)[]
? ToJSON<A>[]
: T;

JSON.stringify函数在查找方法时使用toJSON方法,因此它适用于任何JSON可序列化类型(包括ObjectId、Date(。

它还处理可选属性(灵感来自NonUndefined中的实用程序类型(

最新更新