问题描述
我想创建一个通用的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);
}
}
此类型的通用类型如下:
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
的{{1}}
我尝试过这些类型,但是失败了
I
解决方法
您的代码中缺少的是 save
中的三元语句不理解它所施加的限制,并且没有改进 model
中的 true
类型和 false
分支。
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
要求创建所需但不存在于创建对象中的任意属性。这真的有必要吗?可以支持这一点,但需要更高级的检查。
您还说如果存在 create
,则无法调用 id
。这真的有必要吗,还是可以忽略 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);
}
}
,
更新。很抱歉,如果我误解了您的问题。我为Entity
创建了一个接口(虽然可能是类型)。我还为NotEntity
创建了一个类型,它只是一个没有id
的实体。该类使用Entity
,然后在保存功能中检查id
的类型,如果它是undefined
或它的值等于null
,那么我们在谈论的不是实体,并且调用保存功能。否则,我们有一个id
,我们可以调用update函数。我相信有更好的方法可以做到这一点,但这可能是您自己的初步方法。
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);
}
}