Override the properties of an interface in TypeScript

UPDATE, 2018-08

TypeScript 2.8 introduced Exclude<T, U> which behaves like the Diff<T, U> defined below for all types (not just key types). You should definitely use Exclude<> instead of Diff<> if you are using TypeScript 2.8 or above.

Also, in TypeScript 2.9, keyof any was expanded from string to string | number | symbol, so the below Diff<T, U> caused errors which can be fixed by changing Diff<T extends string, U extends string> to Diff<T extends keyof any, U extends keyof any>. This change has been made below.


ORIGINAL ANSWER, 2018-03

Yes, conditional types will enable this, although you can get this behavior without them as well.

The idea is to pull properties out of the original interface and then replace them with new ones. Like this:

type Diff<T extends keyof any, U extends keyof any> = 
  ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;

interface OriginalInterface {
  title?: string;
  text?: string;
  anotherProperty?: SomeType;
  // lots of other properties
}
interface Extension {
  title?: string | ReactElement<any>;
  text?: string | ReactElement<any>;
}

interface ExtendedInterface extends Overwrite<OriginalInterface, Extension> {};

const ext: ExtendedInterface = {};  // okay, no required properties
ext.text; // string | ReactElement<any> | undefined
ext.title; // string | ReactElement<any> | undefined
ext.anotherProperty; // SomeType | undefined

EDIT: I changed the definition of Overwrite<T,U> to respect the optional/required status of properties from T whose keys are not present in U.

This has the behavior you want, I think. Hope that helps; good luck!


Using TypeScript Omit:

Omit<T,K>

Constructs a type by picking all properties from T and then removing K

// original interface
interface A {
  a: number;
  b: number; // we want string type instead of number
}

// Remove 'b'
type BTemp = Omit<A, 'b'>;

// extends A (BTemp) and redefine b
interface B extends BTemp {
  b: string;
}

const a: B = {
  a: 5,
  b: 'B'
}

enter image description here


Breaking change since 2.9: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#keyof-now-includes-string-number-and-symbol-keys

Use

Extract<keyof T, string> 

to avoid

Type 'keyof T' does not satisfy the constraint 'string'.

Edit: Would have written a comment for this, but lack the necessary reputation