Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

typescript: overwrite a field from an extended interface

I've got an interface which extends 2 other interfaces.

The new interface is identical except for one thing: the field '_id' should be a string instead of an ObjectId (for easier server side operations).

Is it possible to overwrite the type of a field in the new interface? When I do it, tslint tells the new interface doesn't extend properly the previous ones.

Also I'd like to avoid union type such as: _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;
}
like image 466
Florent Arlandis Avatar asked Dec 13 '22 15:12

Florent Arlandis


2 Answers

I would see two options:

Base interfaces

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;
}

Use Omit

Have a look at Exclude Types (https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html) permitting this kind of mappings for types : http://ideasintosoftware.com/typescript-advanced-tricks/

in your case:

interface AchievementFull extends Omit<AchievementDb, '_id'>, Omit<AchievementUserProgress, '_id'>  {
    _id: string;
}
like image 162
Romain Avatar answered Feb 14 '23 13:02

Romain


There are several options.

We could use a generic type parameter on the interfaces to specify the type of id. We can specify a default type parameter to be able to keep using the interface without type parameters if necessary:

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> {
}

Another option is to use Pick and Exclude to remove the field from the interfaces so we can override it as we wish (you will find a similar type defined as Omit by others)

type ExcludeId<T> = Pick<T, Exclude<keyof T, '_id'>>
export interface AchievementFull extends ExcludeId<AchievementDb>, ExcludeId<AchievementUserProgress> {
  _id: string;
}
like image 20
Titian Cernicova-Dragomir Avatar answered Feb 14 '23 14:02

Titian Cernicova-Dragomir