481

Is there a way to change the type of interface property defined in a *.d.ts in typescript?

for example: An interface in x.d.ts is defined as

interface A {
  property: number;
}

I want to change it in the typescript files that I write to

interface A {
  property: Object;
}

or even this would work

interface B extends A {
  property: Object;
}

Will this approach work? It didn't work when I tried on my system. Just want to confirm if it's even possible?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Abdul23
  • 5,265
  • 2
  • 16
  • 23

22 Answers22

920

I use a method that first filters the fields and then combines them.

reference Exclude property from type

interface A {
    x: string
}

export type B = Omit<A, 'x'> & { x: number };

for interface:

interface A {
    x: string
}

interface B extends Omit<A, 'x'> {
  x: number
}
enomad
  • 1,053
  • 9
  • 16
ZSkycat
  • 9,372
  • 2
  • 8
  • 5
  • 23
    It's great to know this. But the problem is it's still not modifying the existing one. – Freewind Oct 04 '18 at 12:07
  • 48
    This was exactly what I was looking for. It's how I expected the typescript `extend` to work by default, but alas, this little `Omit` fixes everything – Dawson B Jul 20 '19 at 15:55
  • 3
    Note: You'll need typescript 3.5.3 above to use this. – Vixson Jul 11 '20 at 05:51
  • Can you still use "B" where "A" is expected? I have 2 types, "ModelShared" and "ModelServer", and a field `serverOnly`. I want server-only to never exist in `ModelShared`, so I use `{serverOnly: never}` => that's excellent for developer experience, they can immediately tell that the field exist, but they should use `ModelServer` instead. But I still have a lot of functions that expect a generic `ModelShared` object. However, all listed answers seems to break inheritance: if you try to pass a `ModelServer`, you get issues, while those types are actually related. I ended up using 3 interfaces. – Eric Burel Sep 24 '21 at 13:51
  • 3
    // Omit a single property: type OmitA = Omit; // Equivalent to: {b: number, c: boolean} // Or, to omit multiple properties: type OmitAB = Omit; // Equivalent to: {c: boolean} – Royer Adames Apr 10 '22 at 21:10
  • I think it could be more useful if We can still validate the presence of the properties as We were using the T1 interface directly because the purpose is just overriding the type not extending the original interface. Eg.: ``interface T1 { a: object }`` // don't works as expected ``type Modify = Omit & R;`` ``type overridenTypeOnly = Modify`` The result would be that the "a" property has its type changed, but should not accept the "b" property because it isn't in the T1 interface. Any solution for this? – Marcos Freitas Jun 22 '22 at 14:46
297
 type ModifiedType = Modify<OriginalType, {
  a: number;
  b: number;
}>
 
interface ModifiedInterface extends Modify<OriginalType, {
  a: number;
  b: number;
}> {}

Inspired by ZSkycat's extends Omit solution, I came up with this:

type Modify<T, R> = Omit<T, keyof R> & R;

// before typescript@3.5
type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R

Example:

interface OriginalInterface {
  a: string;
  b: boolean;
  c: number;
}

type ModifiedType  = Modify<OriginalInterface , {
  a: number;
  b: number;
}>

// ModifiedType = { a: number; b: number; c: number; }

Going step by step:

type R0 = Omit<OriginalType, 'a' | 'b'>        // { c: number; }
type R1 = R0 & {a: number, b: number }         // { a: number; b: number; c: number; }

type T0 = Exclude<'a' | 'b' | 'c' , 'a' | 'b'> // 'c'
type T1 = Pick<OriginalType, T0>               // { c: number; }
type T2 = T1 & {a: number, b: number }         // { a: number; b: number; c: number; }

TypeScript Utility Types


Deep Modification v3

interface Original {
  a: {
    a: string
    b: { a: string }
    c: string
    d: string         // <- keep this one 
  }
}

interface Overrides {
  a: {
    a: { a: number }  // <- overwrite string with object
    b: number         // <- overwrite object with number
    c: number         // <- overwrite string with number
    e: number         // <- new property
  }
}

type ModifiedType = ModifyDeep<Original, Overrides>
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
const example: ModifiedType = {
  a: {
    a: { a: number },
    b: number,
    c: number,
    d: string,
    e: number,
  }
}

Find ModifyDeep below.

Qwerty
  • 29,062
  • 22
  • 108
  • 136
77

You can't change the type of an existing property.

You can add a property:

interface A {
    newProperty: any;
}

But changing a type of existing one:

interface A {
    property: any;
}

Results in an error:

Subsequent variable declarations must have the same type. Variable 'property' must be of type 'number', but here has type 'any'

You can of course have your own interface which extends an existing one. In that case, you can override a type only to a compatible type, for example:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

By the way, you probably should avoid using Object as a type, instead use the type any.

In the docs for the any type it states:

The any type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type-checking during compilation. You might expect Object to play a similar role, as it does in other languages. But variables of type Object only allow you to assign any value to them - you can’t call arbitrary methods on them, even ones that actually exist:

let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
Herb Caudill
  • 50,043
  • 39
  • 124
  • 173
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • 1
    RE *"you probably should avoid using `Object` as a type, instead use the type `any`"* - I would **never** recommend someone to use `any`. There is almost never a reason to use it. The most common reason people use it is a lack of understanding on how to type things. Only use this if you *need* a *dynamic* type. Otherwise, use `unknown` or generic types. – devklick Feb 21 '23 at 15:31
76

The short answer for lazy people like me:

type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> }; 
interface Overrided extends Omit<YourInterface, 'overrideField'> {
  overrideField: <type>
}
Masih Jahangiri
  • 9,489
  • 3
  • 45
  • 51
  • 1
    This actually uses the very same mechanism like my answer [below](https://stackoverflow.com/questions/41285211/overriding-interface-property-type-defined-in-typescript-d-ts-file/55032655#55032655), but requires you to type the field name **twice**. I suggest that you look [below](https://stackoverflow.com/questions/41285211/overriding-interface-property-type-defined-in-typescript-d-ts-file/55032655#55032655) for my `type Overridden = Modify }>` which automatically handles the omit part for you internally. – Qwerty Jul 28 '22 at 13:47
  • 1
    And also this is a 1:1 duplicate of the accepted solution by [ZSkycat](https://stackoverflow.com/questions/41285211/overriding-interface-property-type-defined-in-typescript-d-ts-file/51507473#51507473). – Qwerty Jul 28 '22 at 13:51
45

Extending @zSkycat's answer a little, you can create a generic that accepts two object types and returns a merged type with the members of the second overriding the members of the first.

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;

interface A {
    name: string;
    color?: string;
}

// redefine name to be string | number
type B = Merge<A, {
    name: string | number;
    favorite?: boolean;
}>;

let one: A = {
    name: 'asdf',
    color: 'blue'
};

// A can become B because the types are all compatible
let two: B = one;

let three: B = {
    name: 1
};

three.name = 'Bee';
three.favorite = true;
three.color = 'green';

// B cannot become A because the type of name (string | number) isn't compatible
// with A even though the value is a string
// Error: Type {...} is not assignable to type A
let four: A = three;
ryanjduffy
  • 4,959
  • 3
  • 22
  • 13
  • 2
    Very cool :-) I've done this before with one or two properties with Omit, but this is much cooler :-) I often want to 'extend' a server entity type and change some things to be required or optional on the client. – Simon_Weaver Jan 23 '19 at 04:37
  • 1
    This should be the accepted solution now. Cleanest way to "extend" an interface. – manuhortet Jul 24 '19 at 09:35
35

Omit the property when extending the interface:

interface A {
  a: number;
  b: number;
}

interface B extends Omit<A, 'a'> {
  a: boolean;
}
JmJ
  • 1,989
  • 3
  • 29
  • 51
14

I have created this type that allows me to easily override nested interfaces:

export type DeepPartialAny<T> = {
  [P in keyof T]?: T[P] extends Obj ? DeepPartialAny<T[P]> : any;
};

export type Override<A extends Obj, AOverride extends DeepPartialAny<A>> = { [K in keyof A]:
  AOverride[K] extends never
    ? A[K]
    : AOverride[K] extends Obj
    ? Override<A[K], AOverride[K]>
    : AOverride[K]
};

And then you can use it like that:

interface Foo {
  Bar: {
    Baz: string;
  };
}
type Foo2 = Override<Foo, { Bar: { Baz: number } }>;

const bar: Foo2['Bar']['Baz'] = 1; // number;
Qwerty
  • 29,062
  • 22
  • 108
  • 136
itay oded
  • 978
  • 13
  • 22
  • 1
    Cannot find name `Obj`. – Qwerty Jun 09 '21 at 10:35
  • I was creating a deep modification type myself and I could not make it work without your `DeepPartialAny`. Otherwise I converged to basically the same solution as you, so I decided to not include my version in [my answer](https://stackoverflow.com/questions/41285211/overriding-interface-property-type-defined-in-typescript-d-ts-file/55032655#55032655), but instead update and improve yours. – Qwerty Jun 11 '21 at 00:38
  • This is really hard to follow. Do you think you could make the code a little more verbose? – forresthopkinsa Aug 09 '21 at 07:53
  • I was able to get a less robust (but tolerable) version of this working [like so](http://hastebin.com/axuqilejaq.typescript) – forresthopkinsa Aug 09 '21 at 08:26
  • I had to roll back my edits because the `extends never` doesn't work. It reports the resulting type as `unknown`. Honestly, I have no idea how I did not notice it before. I was normally using this type... Anyway, I made a completely new solution that fixes this. See [Deep Modification v3 here](https://stackoverflow.com/questions/41285211/overriding-interface-property-type-defined-in-typescript-d-ts-file/74216680#74216680). – Qwerty Oct 27 '22 at 04:08
12

For narrowing the type of the property, simple extend works perfect, as in Nitzan's answer:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

For widening, or generally overriding the type, you can do Zskycat's solution:

interface A {
    x: string
}

export type B = Omit<A, 'x'> & { x: number };

But, if your interface A is extending a general interface, you will lose the custom types of A's remaining properties when using Omit.

e.g.

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

export type B = Omit<A, 'x'> & { x: number };

let b: B = { x: 2, y: "hi" }; // no error on b.y! 

The reason is, Omit internally only goes over Exclude<keyof A, 'x'> keys which will be the general string | number in our case. So, B would become {x: number; } and accepts any extra property with the type of number | string | boolean.


To fix that, I came up with a different OverrideProps utility type as following:

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

Example:

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

export type B = OverrideProps<A, { x: number }>;

let b: B = { x: 2, y: "hi" }; // error: b.y should be boolean!
Aidin
  • 25,146
  • 8
  • 76
  • 67
  • 1
    If you add `& N` to your `OverrideProps` type, I think it will also support new props only in N. Or maybe you could union the keys `P in keyof (M & N)`. – charles-allen Apr 27 '22 at 17:14
  • Can you give me a TypeScript Playground link to what you mean, @charles-allen? – Aidin Apr 27 '22 at 19:47
  • 1
    With `& N` it allows the overriding type to narrow props that weren't already narrowed in the base: https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gbhAToglgEwgHgLIBooByAfFALxQDeAUFFANoDSUKAdlANYQgD2AZlNgC6ALihMIAD2AQWaAM4cufQlAD8hRoKijsmqgF8oAMkJUqraYl4BDAMbQAQtbnRJ02QoBKEW90RpMOWBUFgBzKAAfKBYAVwBbACMkfFjEpEioIJDwqITubgAbCGsWEmpaCVEs1lCaKBBRPMLilgMzUEgoAGUYhLJYBGR0LCcXfHKoSuj4pMQ6gC8q4JqDIjMqXxYgzN6ARlEevvIJqYAmXDqGqGCYiAvaRagAcjBuYGtgbie2je4t4B2CVOB16-ROonOl1ENzuC1EAGYoAB6JFQJCIPxQeaZAAW3BiBTQUCSmWWYTaQA – charles-allen Apr 28 '22 at 18:13
  • Got it. Right. Feel free to edit my answer and add this pro-tip. Thanks! :) – Aidin Apr 30 '22 at 15:39
12

Solution for overwriting two or more properties of an interface:

interface BaseInterface {
    a: string;
    b: string;
    c: string;
}


interface ModifiedInterface extends Omit<BaseInterface, 'a' | 'b'> {
    a?: string; // make it optional
    b: boolean; // make it boolean
    d: number;  // add another property
}

From TypeScript documentation

Stelian
  • 668
  • 10
  • 18
5

Date: 19/3/2021. I think the latest typescript(4.1.2) version is supporting interface override in d.ts file.

// in test.d.ts

interface A {
  a: string
}

export interface B extends A {
  a: number
}

// in any ts file
import { B } from 'test.d.ts'

// this will work
const test: B = { a: 3 }

// this will not work
const test1: B = { a: "3" }

yongming zhuang
  • 136
  • 3
  • 5
5

Override as Alias Type

You can use this type alias :

type Override<T, K extends { [P in keyof T]: any } | string> =
  K extends string
    ? Omit<T, K>
    : Omit<T, keyof K> & K;

and use alike below syntax :

Global Interface

interface IFirst {
    username: string;
}

Interface by override the just name

interface ISecond extends Override<IFirst, 'username'> {
    username: number;
}

Type alias override

type IThird = Override<IFirst, { username: boolean }>;

EDIT :

I'm tried to add this alias type as build-in type in typescript by send issue as proposal to the Typescript Repo

mikoloism
  • 128
  • 2
  • 8
3

It's funny I spend the day investigating possibility to solve the same case. I found that it not possible doing this way:

// a.ts - module
export interface A {
    x: string | any;
}

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

interface B extends A {
    x: SomeOtherType;
}

Cause A module may not know about all available types in your application. And it's quite boring port everything from everywhere and doing code like this.

export interface A {
    x: A | B | C | D ... Million Types Later
}

You have to define type later to have autocomplete works well.


So you can cheat a bit:

// a.ts - module
export interface A {
    x: string;
}

Left the some type by default, that allow autocomplete works, when overrides not required.

Then

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

// @ts-ignore
interface B extends A {
    x: SomeOtherType;
}

Disable stupid exception here using @ts-ignore flag, saying us the we doing something wrong. And funny thing everything works as expected.

In my case I'm reducing the scope vision of type x, its allow me doing code more stricted. For example you have list of 100 properties, and you reduce it to 10, to avoid stupid situations

Egor Malkevich
  • 1,516
  • 1
  • 11
  • 24
3

If someone else needs a generic utility type to do this, I came up with the following solution:

/**
 * Returns object T, but with T[K] overridden to type U.
 * @example
 * type MyObject = { a: number, b: string }
 * OverrideProperty<MyObject, "a", string> // returns { a: string, b: string }
 */
export type OverrideProperty<T, K extends keyof T, U> = Omit<T, K> & { [P in keyof Pick<T, K>]: U };

I needed this because in my case, the key to override was a generic itself.

If you don't have Omit ready, see Exclude property from type.

Toni
  • 1,593
  • 1
  • 11
  • 21
3

If you only want to modify the type of an existing property and not remove it, then & is enough:

// Style that accepts both number and percent(string)
type BoxStyle = {
  height?: string | number,
  width?: string | number,
  padding?: string | number,
  borderRadius?: string | number,
}

// These are both valid
const box1: BoxStyle = {height: '20%', width: '20%', padding: 0, borderRadius: 5}
const box2: BoxStyle = {height: 85, width: 85, padding: 0, borderRadius: 5}

// Override height and width to be only numbers
type BoxStyleNumeric = BoxStyle & {
  height?: number,
  width?: number,
}

// This is still valid
const box3: BoxStyleNumeric = {height: 85, width: 85, padding: 0, borderRadius: 5}

// This is not valid anymore
const box4: BoxStyleNumeric = {height: '20%', width: '20%', padding: 0, borderRadius: 5}
Phierru
  • 219
  • 2
  • 5
3

Deep modification v4

I am currently building a more robust solution in my gist that better handles arrays and allows to remove a key or modify its ?optionality.

interface Original {
  x: {
    a: string
    b: string
  }
}

interface Overrides {
  x: {
    a: never // <- this key will be deleted
    b?: string // <- this will become optional
  }
}

/* result = {
  x: {
    b?: string
  }
} */ 

Deep modification v3

*note, version 2 is in the history of this answer.

interface Original {
  a: {
    a: string
    b: { a: string }
    c: string
    d: string         // <- keep this one 
  }
}

interface Overrides {
  a: {
    a: { a: number }  // <- overwrite string with object
    b: number         // <- overwrite object with number
    c: number         // <- overwrite string with number
    e: number         // <- new property
  }
}

type ModifiedType = ModifyDeep<Original, Overrides>
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
Result
const example: ModifiedType = {
  a: {
    a: { a: number },
    b: number,
    c: number,
    d: string,
    e: number,
  }
}
The code
type ModifyDeep<A, B extends DeepPartialAny<A>> = {
  [K in keyof A | keyof B]:          // For all keys in A and B:
    K extends keyof A                // ───┐
      ? K extends keyof B            // ───┼─ key K exists in both A and B
        ? A[K] extends AnyObject     //    │  ┴──┐
          ? B[K] extends AnyObject   //    │  ───┼─ both A and B are objects
            ? ModifyDeep<A[K], B[K]> //    │     │  └─── We need to go deeper (recursively)
            : B[K]                   //    │     ├─ B is a primitive  use B as the final type (new type)
          : B[K]                     //    │     └─ A is a primitive  use B as the final type (new type)  
        : A[K]                       //    ├─ key only exists in A  use A as the final type (original type)   
      : B[K]                         //    └─ key only exists in B  use B as the final type (new type)
}

type AnyObject = Record<string, any>

// This type is here only for some intellisense for the overrides object
type DeepPartialAny<T> = {
  /** Makes each property optional and turns each leaf property into any, allowing for type overrides by narrowing any. */
  [P in keyof T]?: T[P] extends AnyObject ? DeepPartialAny<T[P]> : any
}

*Note, type DeepPartialAny is there just for type hints, but it's not perfect. Technically, the logic of the ModifyDeep type allows to replace leaf nodes {a: string} with objects {a: {b: ... }} and vice versa, but DeepPartialAny will complain when overriding an object with a flat primitive with an error such as this

Type 'number' has no properties in common with type 'DeepPartialAny<{ a: string; }>'

However, you can safely ignore the error (with /// @ts-ignore or remove extends DeepPartialAny constraint altogether. The resulting type is computed correctly anyway.

example

TypeScript Playground

type ModifyDeep<A, B extends DeepPartialAny<A>> = {
  [K in keyof A | keyof B]:
    K extends keyof A
      ? K extends keyof B
        ? A[K] extends AnyObject
          ? B[K] extends AnyObject
            ? ModifyDeep<A[K], B[K]>
            : B[K]
          : B[K]
        : A[K]
      : B[K]
}

type AnyObject = Record<string, any>

type DeepPartialAny<T> = {
  /** Makes each property optional and turns each leaf property into any, allowing for type overrides by narrowing any. */
  [P in keyof T]?: T[P] extends AnyObject ? DeepPartialAny<T[P]> : any
}


interface Original {
  a: {
    a: string
    b: { a: string }
    c: { a: string }
  }
  b: string
  c: { a: string }
}

interface Overrides {
  a: {
    a: { a: number }  // <- overwrite string with object
    b: number         // <- overwrite object with number
    c: { b: number }  // <- add new child property
    d: number         // <- new primitive property
  }
  d: { a: number }    // <- new object property
}

//@ts-ignore // overriding an object with a flat value raises an error although the resulting type is calculated correctly
type ModifiedType = ModifyDeep<Original, Overrides>
//@ts-ignore
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}


// Try modifying the properties here to prove that the type is working
const t: ModifiedType = {
  a: {
    a: { a: 0 },
    b: 0,
    c: { a: '', b: 0},
    d: 0,
  },
  b: '',
  c: { a: '' },
  d: { a: 0 },
}
Qwerty
  • 29,062
  • 22
  • 108
  • 136
3

Try this:

type Override<T extends object, K extends { [P in keyof T]?: any }> = Omit<T, keyof K> & K;

Usage:

type TransformedArticle = Override<Article, { id: string }>;
ktretyak
  • 27,251
  • 11
  • 40
  • 63
  • As a small nit: Optionally, you can change `any` to `unknown`. In doing so, this solution would also be compatible with the rule `typescript-eslint/no-explicit-any`, which is required in some codebases: ```ts export type Override< T extends object, K extends { [P in keyof T]?: unknown } > = Omit & K; ``` – Geraldo B. Landre Feb 28 '23 at 22:38
  • @GeraldoB.Landre, TypeScript is far from ideal, so it's better to remove the `no-explicit-any` rule. – ktretyak Mar 01 '23 at 00:11
2

Sometimes, above solutions can't help. It happens if following case.

interface A {
    [k: string]: string;
}

interface B extends A {}

type C = Omit<B, 'x'> & { x: number }

C will have x with the string type all the time. It is actual if you want to override for example process.env type

To fix that. you need to override interface A in following way, by removing keys in more strict way


interface C {
    x: number;
}

type ExcludeKeys<Type, Keys> = {
    [Property in keyof Type as Exclude<Property, Keys>]: Type[Property];
};

type AA = ExcludeKeys<B, keyof C> & C;
user2434435
  • 127
  • 5
1

extending Qwerty's Modify utility type solution to restrict keys of R to ones present in T and add IntelliSense as well

export type Modify<T, R extends Partial<Record<keyof T, any>>> = Omit<T, keyof R> & R;
Tobiju
  • 21
  • 1
  • 4
0

NOTE: Not sure if the syntax I'm using in this answer was available when the older answers were written, but I think that this is a better approach on how to solve the example mentioned in this question.


I've had some issues related to this topic (overwriting interface properties), and this is how I'm handling it:

  1. First create a generic interface, with the possible types you'd like to use.

You can even use choose a default value for the generic parameter as you can see in <T extends number | SOME_OBJECT = number>

type SOME_OBJECT = { foo: "bar" }

interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
  property: T;
}
  1. Then you can create new types based on that contract, by passing a value to the generic parameter (or omit it and use the default):
type A_NUMBER = INTERFACE_A;                   // USES THE default = number TYPE. SAME AS INTERFACE_A<number>
type A_SOME_OBJECT = INTERFACE_A<SOME_OBJECT>  // MAKES { property: SOME_OBJECT }

And this is the result:

const aNumber: A_NUMBER = {
    property: 111  // THIS EXPECTS A NUMBER
}

const anObject: A_SOME_OBJECT = {
    property: {   // THIS EXPECTS SOME_OBJECT
        foo: "bar"
    }
}

Typescript playground

cbdeveloper
  • 27,898
  • 37
  • 155
  • 336
0

Based on ZSkycat's excellent answer, you can create an abstracted Override generic type that is handy to use and explains clearly the intent of the code.

type Override<T, K extends keyof T, N> = Omit<T, K> & { [K1 in K]: N };

where:

  • T = existing type
  • K = key of type you wish to override
  • N = new type for key of existing type to override

Example usage:

type GraphQLCodegenConfig = Override<CodegenConfig, 'schema', DocumentNode>;

Dan Barclay
  • 5,827
  • 2
  • 19
  • 22
0

Create A modifier Type

type Modify<T, R extends {[P in keyof T]:any} > = Omit<T, keyof R> & R;

and you can

interface ModifiedInterface extends Modify<OriginalType, {
  a: number;
  b: number;
}> {}

it will give you a type autocomplete

0

Better solution would be to use below Modified type(pun intended) of this answer

export type Modify<T, R extends Partial<T>> = Omit<T, keyof R> & R;

This will also check that keys you are overriding is also present in the original interface and hence make sure that if original interface changes the name then you will get the compile time error and you also have to change the name.

Explanation:

Take the following example.

interface OriginalInterface {
    id: string
}

and modified type is as below

interface ModifiedInterface {
    id: number
}

Now, let say in future, OriginalInterface's id gets renamed to uId then using my type utility you will get the error as below

interface ModifiedInterface {
    id: number // Type '{ geo_point1: GeoPoint | null; }' has no properties in common with type 'Partial<Address>'.ts(2559)
}
AndroidEngineX
  • 975
  • 1
  • 9
  • 22
  • This doesn't work for types, extends Partial forces the new property to have the same type as the old one, see [playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBA8mwEsD2A7AhgGwDwBUA0UA0lBAB7AQoAmAzlANYQhIBmUOAfFALxQAKCAMb0sfNACdEmXBwKEuAMlgBbBMFxyOAbgCwAKFCQoAWSRUELEBqgAlEuUq1+Eqdk5deMVevwMmrW0VbXT19Q2gAMSQkHigAb30oJKg0AC4oGmBxBBQAcxDkqAAjdJQAV2UiiHEQgF99MPBI6JwITNjTc0ssKKQCOJTSiqrxKFqOBr1BVHaKTPTe1vbeBL1CtKgARgAmAGY8ROSSrb2DvVqgA) – wanna_coder101 May 11 '23 at 03:55