Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript declaration merging - changing a property type

Is it possible to change the type of a property that is declared in an interface? I don't know how to explain this in words, so here's a simplified example of what I'm trying to do that doesn't work:

Assume that the Movie namespace is defined in a 3rd-party library that I don't control:

declare namespace Movie {
  interface Character {
    doSomething(): Promise<void>;
    friend: Character;
  }
}

Now in my application, I want my character to be Super, with Super friends. So I tried to do this in my own typings:

declare namespace Movie {
  interface Character {
    doAnExtraThing(): Promise<void>; // OK to add a new method
    nickname: string;                // OK to add a new property
    friend: SuperCharacter;          // NOT OK to override type with a subtype
  }

  interface SuperCharacter extends Character {
    doSomethingSuper(): Promise<void>;
  }
}

That doesn't work though, because TypeScript won't allow me to override the friend property type with SuperCharacter, even though by definition a SuperCharacter is a Character. TypeScript complains with this error:

[ts] Subsequent variable declarations must have the same type. Variable
'friend' must be of type 'Character', but here has type 'SuperCharacter'.

I was hoping that by having SuperCharacter extend the original Character interface I wouldn't have this problem, but I do.

Hopefully it's clear what I'm trying to achieve. Is there a way to do it?

like image 542
Merott Avatar asked Apr 13 '17 18:04

Merott


1 Answers

There's no reason to specify explicitly that friend can be a SuperCharacter since SuperCharacter is already a Character.

So you can remove this block entirely:

interface Character { friend: SuperCharacter; }

As proof, we can step away from interfaces and look at an implementation with classes that will compile properly:

class Character {
  friend?: Character

  constructor(friend?: Character) {
    this.friend = friend
  }

  doSomething() {}
}

class SuperCharacter extends Character {
  doSomethingSuper() {}
}


new Character(new Character())
new Character(new SuperCharacter())
new SuperCharacter(new Character())
new SuperCharacter(new SuperCharacter())
like image 101
fny Avatar answered Nov 03 '22 00:11

fny