通用递归修改 Typescript 中的给定 type/interface
Generic to recursively modify a given type/interface in Typescript
我正在努力制作一个泛型,它会递归地修改在嵌套的递归数据结构中找到的所有元素。这是我的数据结构的示例。使用此递归数据定义,任何 post 都可以有无限数量的评论和回复。
type Post = {
user: string,
content: string,
comments: Comment[],
reactions: Reaction[],
}
type Comment = {
user: string,
content: string,
replies: Comment[],
}
type Reaction = {
user: string,
reaction: "laugh" | "cry" | "smile",
}
我想要的是一个通用包装器,我可以将其与这些和 任何其他数据类型 一起使用,它将每个 user
字段替换为其他内容。我可以为最高级别执行此操作:
type UserFilled<T> = Omit<"user", T> & { user: { id: string, name: string }}
但这只会更改 Post 的 user
字段。我还希望它向下爬行并为每个评论替换更改 user
字段,如果有更多字段,则用于反应、喜欢或任何其他结构 user
他们。
我见过 但我无法使用联合将修改后的 属性 添加回来,我想知道是否有更直接的方法来做到这一点,而不仅仅是省略但也替换该字段?
例如,使用泛型我希望能够做到这一点:
const post: Post = {
user: "1234",
content: "this is a post",
comments: [{
user: "3456",
content: "I agree",
replies: [{
user: "1234",
content: "thanks",
}],
}],
reactions: [{
user: "5678",
reaction: "smile",
}],
};
const postWUserInfo: UserFilled<Post> = {
user: { id: "1234", name: "Bob" },
content: "this is a post",
comments: [{
user: { id: "3456", name: "Jim" },
content: "I agree",
replies: [{
user: { id: "1234", name: "Bob" },
content: "thanks",
}],
}],
reactions: [{
user: { id: "5678", name: "Jim" },
reaction: "smile",
}],
};
您可以创建一个可以递归检查和替换密钥的 DeepReplace 实用程序。另外我强烈建议只替换值并确保密钥保持不变。
// "not object"
type Primitive = string | Function | number | boolean | Symbol | undefined | null
// If T has key K ("user"), replace it
type ReplaceKey<T, K extends string, R> = T extends Record<K, unknown> ? Omit<T, K> & Record<K, R> : T
// Check and replace object values
type DeepReplaceHelper<T, K extends string, R, ReplacedT = ReplaceKey<T, K, R>> = {
[Key in keyof ReplacedT]: ReplacedT[Key] extends Primitive ? ReplacedT[Key] : ReplacedT[Key] extends unknown[] ? DeepReplace<ReplacedT[Key][number], K, R>[] : DeepReplace<ReplacedT[Key], K, R>
}
// T = object, K = key to replace, R = replacement value
type DeepReplace<T, K extends string, R> = T extends Primitive ? T : DeepReplaceHelper<T, K, R>
// Define new type for "user" key
interface UserReplacement {
id: string
name: string
}
type UserFilled<T> = DeepReplace<T, "user", UserReplacement>
我正在努力制作一个泛型,它会递归地修改在嵌套的递归数据结构中找到的所有元素。这是我的数据结构的示例。使用此递归数据定义,任何 post 都可以有无限数量的评论和回复。
type Post = {
user: string,
content: string,
comments: Comment[],
reactions: Reaction[],
}
type Comment = {
user: string,
content: string,
replies: Comment[],
}
type Reaction = {
user: string,
reaction: "laugh" | "cry" | "smile",
}
我想要的是一个通用包装器,我可以将其与这些和 任何其他数据类型 一起使用,它将每个 user
字段替换为其他内容。我可以为最高级别执行此操作:
type UserFilled<T> = Omit<"user", T> & { user: { id: string, name: string }}
但这只会更改 Post 的 user
字段。我还希望它向下爬行并为每个评论替换更改 user
字段,如果有更多字段,则用于反应、喜欢或任何其他结构 user
他们。
我见过
例如,使用泛型我希望能够做到这一点:
const post: Post = {
user: "1234",
content: "this is a post",
comments: [{
user: "3456",
content: "I agree",
replies: [{
user: "1234",
content: "thanks",
}],
}],
reactions: [{
user: "5678",
reaction: "smile",
}],
};
const postWUserInfo: UserFilled<Post> = {
user: { id: "1234", name: "Bob" },
content: "this is a post",
comments: [{
user: { id: "3456", name: "Jim" },
content: "I agree",
replies: [{
user: { id: "1234", name: "Bob" },
content: "thanks",
}],
}],
reactions: [{
user: { id: "5678", name: "Jim" },
reaction: "smile",
}],
};
您可以创建一个可以递归检查和替换密钥的 DeepReplace 实用程序。另外我强烈建议只替换值并确保密钥保持不变。
// "not object"
type Primitive = string | Function | number | boolean | Symbol | undefined | null
// If T has key K ("user"), replace it
type ReplaceKey<T, K extends string, R> = T extends Record<K, unknown> ? Omit<T, K> & Record<K, R> : T
// Check and replace object values
type DeepReplaceHelper<T, K extends string, R, ReplacedT = ReplaceKey<T, K, R>> = {
[Key in keyof ReplacedT]: ReplacedT[Key] extends Primitive ? ReplacedT[Key] : ReplacedT[Key] extends unknown[] ? DeepReplace<ReplacedT[Key][number], K, R>[] : DeepReplace<ReplacedT[Key], K, R>
}
// T = object, K = key to replace, R = replacement value
type DeepReplace<T, K extends string, R> = T extends Primitive ? T : DeepReplaceHelper<T, K, R>
// Define new type for "user" key
interface UserReplacement {
id: string
name: string
}
type UserFilled<T> = DeepReplace<T, "user", UserReplacement>