typescript: overwrite a field from an extended interface

typescript extend interface
typescript extend namespace
typescript interface constructor
typescript interface array
typescript extend type definition
typescript extend object
typescript extend enum
typescript omit

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

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

(suggestion) Overrides in extending class and interface definitions , Using this example: interface A { build(): SomeType; } interface B extends A{ I'm mostly anxious to suggest ideas because I think TypeScript is awesome and I and if you override a property then its probably thought through and dealt with  In a number of places we are using a third party typing for some interface that declares one of it's sub properties or methods as as type any and we would like to create more strongly typed interfaces based on these. For example the angular.ui.IStateService interface has a property called current which in turn has a property called data for storing arbitrary .wait for it data (shocking I know).

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

Declaration Merging · TypeScript, The compiler will issue an error if the interfaces both declare a non-function already exists with the given name, it is further extended by taking the existing  One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

The fact that you are trying to overwrite the field type in AchievementFull interface is a sign of a code smell and defeats the purpose of extending the other interfaces. You can't change _id type from ObjectID to string in a child interface. Maybe you should consider organising your interfaces differently.

TypeScript doesn't allow you to overwrite parent interface property in a child interface.

Interfaces · TypeScript, It still represents having a single property called label that is of type string . Notice we You can still override it with a type assertion, though: a = ro as An interface can extend multiple interfaces, creating a combination of all of the interfaces. In TypeScript, we can use common object-oriented patterns. One of the most fundamental patterns in class-based programming is being able to extend existing classes to create new ones using inheritance. Let’s take a look at an example: class Animal { move (distanceInMeters: number = 0) { console.log (`Animal moved $ {distanceInMeters}m

Based on few other answers, I think this is cleaner (types based on my real code):

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

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

I'm using this type for Angular events so I don't need to cast type of target like in docs:

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

and use this:

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

Classes · TypeScript, This class has three members: a property called greeting , a constructor, and a method greet . Because Dog extends the functionality from Animal , we were able to create an instance The example also shows how to override methods in the base class with Abstract methods share a similar syntax to interface methods. The engine field in the Auto class accepts any type that implements a TypeScript interface named IEngine and the constructor accepts any object that implements an IAutoOptions interface. Both of these interfaces are shown next:

TypeScript - Interface Extending Interfaces, In TypeScript, an interface can extend other interfaces as well. //implementing extended interface class RadioButton { constructor(h, w, label) TypeScript - Method Decorator · Obtaining Property Descriptor by using Object. Extend from a type and override some fields; Turn optional field to required; Only make some specific fields to optional; Suggestion. Support multiple field definition in one type, the latter definition (same fields) will overwrite the previous; Support identifier! for type definition which will remove null and undefined. Code

typescript-cheatsheet, types from an extended interface. One of the most common reasons is to overwrite a property. To merge the namespaces, type definitions from exported interfaces declared in each namespace are themselves merged, forming a single namespace with merged interface definitions inside. To merge the namespace value, at each declaration site, if a namespace already exists with the given name, it is further extended by taking the existing namespace and adding the exported members of the second namespace to the first.

TypeScript, You can extend this simple type system with enumerated values and four kinds of The interface includes two members: a property called Id and a method called To override a public member, simply define a member in the derived class  An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces. Use the extends keyword to implement inheritance among interfaces. Syntax: Single Interface Inheritance Child_interface_name extends super_interface_name

Comments
  • What you want to achieve will not be possible because all types should be better strictly defined. That's why the interface is defined to help you and others to not violate some contract rules. And you are violating the "Interface segregation principle" from SOLID principles.
  • While I don't agree you can't do it you are right about it being a code smell :)
  • You're right, your solution with generic type param is actually quite readable.