Overriding interface property type defined in Typescript d.ts file
Solution 1
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
}
Solution 2
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 [email protected] 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; }
v2.0 Deep Modification
interface Original {
a: {
b: string
d: {
e: string // <- will be changed
}
}
f: number
}
interface Overrides {
a: {
d: {
e: number
f: number // <- new key
}
}
b: { // <- new key
c: number
}
}
type ModifiedType = ModifyDeep<Original, Overrides>
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
// ModifiedType =
{
a: {
b: string
d: {
e: number
f: number
}
}
b: {
c: number
}
f: number
}
Find ModifyDeep
below.
Solution 3
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'.
Solution 4
The short answer for lazy people like me:
type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> };
interface Overrided extends Omit<YourInterface, 'overrideField'> {
overrideField: <type>
}
Solution 5
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;
Related videos on Youtube
Abdul23
Updated on November 04, 2021Comments
-
Abdul23 over 2 years
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 asinterface 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?
-
Freewind over 5 yearsIt's great to know this. But the problem is it's still not modifying the existing one.
-
aruno over 5 yearsVery 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.
-
Dawson B almost 5 yearsThis was exactly what I was looking for. It's how I expected the typescript
extend
to work by default, but alas, this littleOmit
fixes everything 🙌 -
manuhortet almost 5 yearsThis should be the accepted solution now. Cleanest way to "extend" an interface.
-
mhodges over 4 yearsExtending the interface was exactly what I was looking for, thanks!
-
Dominic over 4 yearsNoob here but you're change from an interface to a type in your example no? Or is there no difference?
-
Qwerty over 4 years@Dominic Good point, I have updated the answer. Two Interfaces with same name can merge. typescriptlang.org/docs/handbook/…
-
Vixson almost 4 yearsNote: You'll need typescript 3.5.3 above to use this.
-
dwoodwardgb almost 4 yearsThis is exactly what I was looking for, I can't thank you enough :D :D :D
-
Toni over 3 years@dwoodwardgb glad it was useful for someone else :-)
-
Ambrus Tóth about 3 yearsThis should become a TS feature
-
Steve Moretz about 3 yearsIs it 20201 yet? Lol. You're talking to us from the future.Are there flying cars there?
-
yongming zhuang about 3 yearsHahaha, I would like to say Yes, cars are flying and people are traveling by jet suit. :-) Thanks for pointing out the typo!!!
-
victor zadorozhnyy almost 3 yearsI'm from the future. It does not work
incorrectly extends interface
-
yongming zhuang almost 3 yearsWhat is your
ts
version? Did you declare the interface in thed.ts
file? @victorzadorozhnyy -
victor zadorozhnyy almost 3 years@yongmingzhuang 4.2 I've try to do it in tsx
-
yongming zhuang almost 3 years@victorzadorozhnyy, I am not sure why, but interface overrides seem only work in the
d.ts
file. -
Qwerty almost 3 yearsCannot find name
Obj
. -
Qwerty almost 3 yearsI 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, but instead update and improve yours. -
forresthopkinsa over 2 yearsThis is really hard to follow. Do you think you could make the code a little more verbose?
-
forresthopkinsa over 2 yearsI was able to get a less robust (but tolerable) version of this working like so
-
Eric Burel over 2 yearsCan 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 inModelShared
, so I use{serverOnly: never}
=> that's excellent for developer experience, they can immediately tell that the field exist, but they should useModelServer
instead. But I still have a lot of functions that expect a genericModelShared
object. However, all listed answers seems to break inheritance: if you try to pass aModelServer
, you get issues, while those types are actually related. I ended up using 3 interfaces. -
Admin over 2 yearsWhat is
Modify
here? -
Qwerty over 2 years@Donnovan It's a custom type, go through the answer again find ->
type Modify<T, R> = Omit<T, keyof R> & R;
-
Royer Adames about 2 years// Omit a single property: type OmitA = Omit<Test, "a">; // Equivalent to: {b: number, c: boolean} // Or, to omit multiple properties: type OmitAB = Omit<Test, "a"|"b">; // Equivalent to: {c: boolean}
-
charles-allen about 2 yearsIf you add
& N
to yourOverrideProps
type, I think it will also support new props only in N. Or maybe you could union the keysP in keyof (M & N)
. -
Aidin about 2 yearsCan you give me a TypeScript Playground link to what you mean, @charles-allen?
-
charles-allen about 2 yearsWith
& N
it allows the overriding type to narrow props that weren't already narrowed in the base: typescriptlang.org/play?#code/… -
Aidin about 2 yearsGot it. Right. Feel free to edit my answer and add this pro-tip. Thanks! :)
-
Marcos Freitas almost 2 yearsI 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 expectedtype Modify<T, R> = Omit<T, keyof R> & R;
type overridenTypeOnly = Modify<T1, { a: number, b: string }>
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?