如何处理 TypeScript 中重复的相似类型
How to handle repeated similar types in TypeScript
在我的应用程序的简化版本中,我从服务器接收评论并且每个评论都有回复数组作为 属性。我想正常化我的状态,这样我有评论没有回复作为状态的一部分,回复作为状态的第二部分。
当我创建评论时,我只提交 author
和 body
。当我收到来自服务器的评论时,我获得了额外的属性以及 replies
数组,并且在我想要没有回复的评论的状态下。
因为如果在我的 types.ts
文件中我有 4 个不同的界面。一个用于新评论,另一个用于进入状态的无回复评论,第三个用于从服务器收到的整个评论,第四个仅用于回复。
export interface NewComment {
author: string,
body: string
}
export interface CommentWithoutReplies {
id: string,
author: string,
body: string,
postedAt: number,
replies_count: number,
}
export interface Comment {
id: string,
author: string,
body: string,
postedAt: number,
replies_count: number,
replies: Reply[]
}
export interface Reply {
id: string
comment_id: string,
author: string,
body: string,
postedAt: number,
}
我正在创建一个评论上下文文件,它现在看起来像这样并通过了检查:
import { createContext, useCallback, useState, FC } from "react";
import {Comment, CommentWithoutReplies, CommentContextState, CommentContextDispatch} from "../types/types"
const defaultStateValue: CommentContextState = {
comments: [],
};
const defaultDispatchValue: CommentContextDispatch = {
getComments: () => undefined,
addComment: () => undefined,
};
export const CommentStateContext = createContext<CommentContextState>(defaultStateValue);
export const CommentDispatchContext = createContext<CommentContextDispatch>(defaultDispatchValue);
const CommentProvider: FC = ({children}) => {
const [comments, setComments] = useState<CommentWithoutReplies[]>(defaultStateValue.comments);
const getComments = useCallback(
(data: Comment[]) => {
setComments(() => {
return data.map(comment => {
const {replies, ...commentWithoutReplies} = comment;
return commentWithoutReplies
})
});
},
[setComments]
);
const addComment = useCallback(
(data: CommentWithoutReplies) => {
setComments((currentComments: CommentWithoutReplies[]) => {
return [...currentComments, data];
});
},
[setComments]
);
return (
<CommentDispatchContext.Provider
value={{
getComments,
addComment,
}}
>
<CommentStateContext.Provider
value={{
comments,
}}
>
{children}
</CommentStateContext.Provider>
</CommentDispatchContext.Provider>
);
};
export default CommentProvider;
有没有比拥有 4 个不同接口更优雅的方式来处理这些类型?
你现在所做的对我来说看起来很合理 - 你正在使用多个单独的数据形状,因此在大多数情况下你需要在某处定义这些形状中的每一个。但是,您可以通过在不需要时不注意类型来减少重复,例如 this
setComments((currentComments: CommentWithoutReplies[]) => {
可以
setComments((currentComments) => {
并且您可以通过从包含所有公共属性的基类型开始更简洁地定义类型 - id
、author
、postedAt
.
type BaseComment {
id: string,
author: string,
postedAt: number,
}
export type CommentWithoutReplies = BaseComment & {
body: string,
replies_count: number,
}
export type Comment = BaseComment & {
body: string,
replies_count: number,
replies: Reply[]
}
export type Reply = BaseComment & {
comment_id: string,
body: string,
}
不知道您还有什么其他代码,但如果此处的类型没有导入到其他地方,您也可以在上下文文件本身中定义它们,而不必导入它们。
在我的应用程序的简化版本中,我从服务器接收评论并且每个评论都有回复数组作为 属性。我想正常化我的状态,这样我有评论没有回复作为状态的一部分,回复作为状态的第二部分。
当我创建评论时,我只提交 author
和 body
。当我收到来自服务器的评论时,我获得了额外的属性以及 replies
数组,并且在我想要没有回复的评论的状态下。
因为如果在我的 types.ts
文件中我有 4 个不同的界面。一个用于新评论,另一个用于进入状态的无回复评论,第三个用于从服务器收到的整个评论,第四个仅用于回复。
export interface NewComment {
author: string,
body: string
}
export interface CommentWithoutReplies {
id: string,
author: string,
body: string,
postedAt: number,
replies_count: number,
}
export interface Comment {
id: string,
author: string,
body: string,
postedAt: number,
replies_count: number,
replies: Reply[]
}
export interface Reply {
id: string
comment_id: string,
author: string,
body: string,
postedAt: number,
}
我正在创建一个评论上下文文件,它现在看起来像这样并通过了检查:
import { createContext, useCallback, useState, FC } from "react";
import {Comment, CommentWithoutReplies, CommentContextState, CommentContextDispatch} from "../types/types"
const defaultStateValue: CommentContextState = {
comments: [],
};
const defaultDispatchValue: CommentContextDispatch = {
getComments: () => undefined,
addComment: () => undefined,
};
export const CommentStateContext = createContext<CommentContextState>(defaultStateValue);
export const CommentDispatchContext = createContext<CommentContextDispatch>(defaultDispatchValue);
const CommentProvider: FC = ({children}) => {
const [comments, setComments] = useState<CommentWithoutReplies[]>(defaultStateValue.comments);
const getComments = useCallback(
(data: Comment[]) => {
setComments(() => {
return data.map(comment => {
const {replies, ...commentWithoutReplies} = comment;
return commentWithoutReplies
})
});
},
[setComments]
);
const addComment = useCallback(
(data: CommentWithoutReplies) => {
setComments((currentComments: CommentWithoutReplies[]) => {
return [...currentComments, data];
});
},
[setComments]
);
return (
<CommentDispatchContext.Provider
value={{
getComments,
addComment,
}}
>
<CommentStateContext.Provider
value={{
comments,
}}
>
{children}
</CommentStateContext.Provider>
</CommentDispatchContext.Provider>
);
};
export default CommentProvider;
有没有比拥有 4 个不同接口更优雅的方式来处理这些类型?
你现在所做的对我来说看起来很合理 - 你正在使用多个单独的数据形状,因此在大多数情况下你需要在某处定义这些形状中的每一个。但是,您可以通过在不需要时不注意类型来减少重复,例如 this
setComments((currentComments: CommentWithoutReplies[]) => {
可以
setComments((currentComments) => {
并且您可以通过从包含所有公共属性的基类型开始更简洁地定义类型 - id
、author
、postedAt
.
type BaseComment {
id: string,
author: string,
postedAt: number,
}
export type CommentWithoutReplies = BaseComment & {
body: string,
replies_count: number,
}
export type Comment = BaseComment & {
body: string,
replies_count: number,
replies: Reply[]
}
export type Reply = BaseComment & {
comment_id: string,
body: string,
}
不知道您还有什么其他代码,但如果此处的类型没有导入到其他地方,您也可以在上下文文件本身中定义它们,而不必导入它们。