【发布时间】:2021-12-02 06:53:52
【问题描述】:
我正在编写insert post 用例的实现。我正在研究 Clean Architecture,我想创建一个博客 API,您可以在其中创建、阅读、更新和删除帖子。
清洁架构原则说,您必须将应用程序隔离到多个层中,每个层都负责尽可能地抽象您的代码。我将我的代码分为三个主要层:core(最“不可变”的抽象,我在其中定义数据模型和用例),app(我在其中编写主要应用程序代码,如验证和其他内容)和resources(存放不太抽象的代码,如数据库连接、适配器等)。
一般数据被抽象到core 层,因为它是我的API 中最基本的信息。但是一般数据(特别是 id、created_at、updated_at、is_deleted 字段)被抽象到 app 层,因为它依赖于数据库 - id 字段可以是一个数字(对于一般SQL 数据库)或字符串(例如MongoDB)。然后,我只是将这两个抽象结合到一个模型中来创建“确定的”应用程序模型。
但是这样做有一个问题:用例抽象使用id 数据来定义其实现规则。例如,您有InsertPost 用例,其中返回数据是新创建帖子的id。由于id 只定义在app 层,而Clean Architecture 不允许你从更多外部层请求数据,这个抽象怎么知道他返回的是什么类型的id?我找到的解决方案是将id类型抽象为两种可能的类型(number或string),并且只在app层提供确定的类型。
这是所描述的InsertPost 用例:
// ----- IMPORTS -----
type Id = number | string;
interface Post<IdType extends Id = Id> {
title: string
description: string
body: string
author_id: IdType
}
interface SuccessfulResponse<T> {
results: Array<T>
}
// -------------------
export namespace InsertPost {
export type Params<IdParam extends Id = Id> = Post<IdParam>
}
export interface InsertPost<IdType extends Id> {
insert: (params: InsertPost.Params) => Promise<SuccessfulResponse<IdType>>
}
我从InsertPost 命名空间中编写了Params 类型,使其具有只接受从Id 类型派生的类型的通用类型。当没有给出类型时,IdType 也应该有一个默认类型。我的问题是 TypeScript 使用默认的 Id 类型,无论是否提供了有效的替代类型。
这是我对InsertPost 用例的实现:
// ----- IMPORTS -----
type Id = number | string;
interface Post<IdType extends Id = Id> {
title: string
description: string
body: string
author_id: IdType
}
interface SuccessfulResponse<T> {
results: Array<T>
}
namespace DefaultData {
export type Id = number
export type IsDeleted = boolean
export type CreatedAt = Date
export type UpdatedAt = Date
}
interface DefaultData {
id: DefaultData.Id
is_deleted: DefaultData.IsDeleted
created_at: DefaultData.CreatedAt
updated_at: DefaultData.UpdatedAt
}
interface InsertPostRepository {
insertPost: (user: Post) => Promise<DefaultData.Id>
}
function successfulResponseDataFormatter<T>(data: Array<T> | T): SuccessfulResponse<T> {
return {
results: Array.isArray(data) ? data : [data]
};
}
namespace InsertPost {
export type Params<IdParam extends Id = Id> = Post<IdParam>
}
interface InsertPost<IdType extends Id> {
insert: (params: InsertPost.Params) => Promise<SuccessfulResponse<IdType>>
}
// -------------------
export class InsertPostService implements InsertPost<DefaultData.Id> {
constructor(private readonly insertPostRepository: InsertPostRepository) {}
async insert(params: InsertPost.Params<DefaultData.Id>): Promise<SuccessfulResponse<DefaultData.Id>> { // TypeScript throws an error at this line
const { title, description, body, author_id } = params;
// Validation functions here
const id = await this.insertPostRepository.insertPost({
title,
description,
body,
author_id
});
return successfulResponseDataFormatter(id);
}
}
TypeScript 抛出以下错误:
“InsertPostService”类型中的属性“插入”不能分配给基类型“InsertPost”中的相同属性。
类型 '(params: Params) => Promise
' 不可分配给类型 '(params: Params) => Promise '。
参数“params”和“params”的类型不兼容。
类型“Params”不可分配给类型“Params”。
类型 'Id' 不能分配给类型 'number'。
类型“字符串”不可分配给类型“数字”。
根据我的测试,这只发生在我从namespace 中导出类型时(不幸的是,我需要使用它)。我真的看不出哪里出错了;有什么我缺少的代码吗,或者这是一个 TypeScript 错误?
【问题讨论】:
-
请提供minimal reproducible example,清楚地表明您面临的问题。理想情况下,有人可以将代码放入像The TypeScript Playground (link here!) 这样的独立IDE 中,然后立即着手解决问题,而无需首先重新创建它。所以应该没有错别字、不相关的错误或未声明的类型或值。
-
@jcalz 完成!请说现在是否足够好。
-
@jcalz 没有想到这个解决方案,我感觉很愚蠢!是的,这解决了我的问题。非常感谢!
标签: typescript typescript-generics