如何编写类型安全的联合类型化保存方法
Posted
技术标签:
【中文标题】如何编写类型安全的联合类型化保存方法【英文标题】:How to write a typesafe union typed save metod 【发布时间】:2021-02-18 02:31:12 【问题描述】:我想创建一个通用的 http 服务,并且我想有一个类似 save(value)
的方法,它在下面调用 create(value)
或 update(value)
。不同的是,我想选择配置 create
接受的值的类型,它可以与更新值完全不同。
像这样:
class HttpService
create(model)
...
update(model)
...
save(model)
if(model.id === null || model.id === undefined)
return this.create(model);
return this.update(model);
this 的泛型是这样的:
class HttpService<I, E, C>
create(model: E | C) : E;
update(model: E) : E;
save(model: E | C): E;
我想合并这两种情况:
-
创建和更新共享同一个接口,其中实体(称为
E
)ID 具有属性名称Id
。这个 id 是通用的(称为I
),它的类型是I | null | undefined
。因此,此接口扩展了MaybeEntity<I>
。
创建和更新接受不同的接口。当使用 ceate 函数时,它应该接受接口C extends NotEnity
,其中Id
不存在,null
或undefined
并返回E
。此更新仅接受E extends Entity<I>
,其中Id
必须为I
我已经尝试过这些类型,但我失败了
export type Id = 'id';
export type Entity<I> = Record<Id, I>;
export type NotEntity = Partial<Record<Id, null>>;
export type MaybeEntity<I> = Partial<Record<Id, I | null >>;
class HttpService<I, E extends MaybeEntity<I>, C extends NotEntity = (E & NotEntity )>
create(model: C | E & NotEntity)
update(model: E & Entity<I>)
save(model: C | E)
return model.id === undefined && model.id === null
? this.create(model)
: this.update(model);
【问题讨论】:
打字稿种子typescriptlang.org/play?#code/… 【参考方案1】:更新。抱歉,如果我错过了您的问题。我为Entity
创建了一个接口(尽管它可能是一个类型)。我还为NotEntity
创建了一个类型,它只是一个没有id
的实体。该类接受Entity
,然后在保存函数中检查id
的类型,如果它是undefined
或其值等于null
,那么我们正在讨论一个非实体并调用保存函数。否则我们有一个id
,我们可以调用更新函数。我确信有更好的方法来做到这一点,但这可能是您自己的初步方法。
export interface Entity<I>
id?: I,
[key: string]: any
export type NotEntity<I> = Omit<Entity<I>, 'id'>;
export class HttpService<I, E extends Entity<I>>
create(model: NotEntity<I>)
update(model: Entity<I>)
save(model: E)
return typeof model.id === undefined || model.id === null
? this.create(model)
: this.update(model);
【讨论】:
【参考方案2】:您的代码中缺少的是save
中的三元语句不理解它所施加的限制,并且没有改进true
和false
分支中model
的类型。
Typescript 可以理解简单的属性检查条件,但是当您将它们与 &&
结合使用时,它会停止尝试。我们需要将这个多重条件检查提取到它自己的类型保护函数中,并告诉 Typescript true
或 false
是什么意思。
const hasId = <E extends MaybeEntity<any>>( model: E ): model is E & id: NonNullable<E['id']> =>
return 'id' in model && model.id === undefined && model.id === null;
现在您允许C
要求创建所需但在创建的对象中不存在的任意属性。这真的有必要吗?可以支持这一点,但需要更高级的检查。
您还说如果存在id
,则无法调用create
。这真的有必要吗,还是可以忽略id
? (例如,如果将现有对象克隆为新 id)。
假设这两个都不是绝对必要的,这段代码可以工作:
export class HttpService<I, E extends MaybeEntity<I>>
create(model: E)
update(model: E & Entity<I>)
save(model: E)
return hasId(model)
? this.update(model)
: this.create(model);
Typescript Playground Link
【讨论】:
以上是关于如何编写类型安全的联合类型化保存方法的主要内容,如果未能解决你的问题,请参考以下文章