打字稿:覆盖扩展接口中的字段

时间:2018-07-30 10:19:50

标签: typescript interface

我有一个扩展2个其他接口的接口。

除了以下几点外,新接口是相同的:字段'_id'应该是字符串而不是ObjectId(以便于服务器端操作)。

是否可以覆盖新界面中的字段类型?当我这样做时,tslint会告知新界面未正确扩展先前的界面。

我还想避免使用诸如_id : ObjectId | string

这样的联合类型
export interface AchievementDb {
    _id: ObjectID; 
    title: string;
    description: string;
    // more stuff
}

export interface AchievementUserProgress {
    _id: ObjectID;
    progress: number;
    status: UserAchievementStatus;
    // more stuff
}

export interface AchievementFull extends AchievementDb, AchievementUserProgress {
    _id: string;
}

4 个答案:

答案 0 :(得分:4)

我会看到两个选择:

基本接口

interface WithID {
    _id: ObjectID;
}

interface AchievementDbBase {
    title: string;
    description: string;
    // more stuff
}

interface AchievementDb extends AchievementDbBase, WithID {

}

interface AchievementUserProgressBase {
    progress: number;
    // more stuff
}

interface AchievementUserProgress extends AchievementUserProgressBase, WithID {

}

interface AchievementFull extends AchievementDbBase, AchievementUserProgressBase {
    _id: string;
}

使用忽略

看看排除类型(https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html)允许对类型进行这种类型的映射: http://ideasintosoftware.com/typescript-advanced-tricks/

在您的情况下:

interface AchievementFull extends Omit<AchievementDb, '_id'>, Omit<AchievementUserProgress, '_id'>  {
    _id: string;
}

答案 1 :(得分:3)

有几种选择。

我们可以在接口上使用通用类型参数来指定id的类型。我们可以指定一个默认的类型参数,以便在必要时可以继续使用不带类型参数的接口:

export interface AchievementDb<T = ObjectID> {
  _id: T; 
  title: string;
  description: string;
  // more stuff
}
export interface AchievementUserProgress<T = ObjectID> {
  _id: T;
  progress: number;
  status: UserAchievementStatus;
  // more stuff
}

export interface AchievementFull extends AchievementDb<string>, AchievementUserProgress<string> {
}

另一种选择是使用PickExclude从接口中删除该字段,以便我们可以按需覆盖它(您会发现其他人定义为Omit的相似类型)

type ExcludeId<T> = Pick<T, Exclude<keyof T, '_id'>>
export interface AchievementFull extends ExcludeId<AchievementDb>, ExcludeId<AchievementUserProgress> {
  _id: string;
}

答案 2 :(得分:1)

您试图覆盖AchievementFull接口中的字段类型的事实表明代码有味道,并且无法扩展其他接口。您不能在子界面中将_id的类型从ObjectID更改为string。 也许您应该考虑以不同的方式组织您的界面。

TypeScript不允许您覆盖子接口中的父接口属性。

答案 3 :(得分:0)

基于其他一些答案,我认为这更干净(基于我的真实代码的类型):

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

interface HTMLEvent<T> extends Omit<Event, 'target'> {
    target: T;
}

我将这种类型用于Angular事件,因此不需要像docs中那样强制转换目标类型:

onKey(event: KeyboardEvent) { // with type info
    this.values += (<HTMLInputElement>event.target).value + ' | ';
}

并使用它:

onInput(event: HTMLEvent<HTMLInputElement>) {
   this.value = event.target.value;
}