How should I fill out a module declaration file for a JS library with Typescript-incompatible inheritance practices?

216 views Asked by At

I'm filling out a module declaration of a 3rd-party JS library, and the library contains subclasses that (by Typescript's reckoning) incompatibly override methods from a parent class. There are many instances of this, but a simple example is the following:

Base class:

class Entity {
  ...

  /**
  * Test whether a given User has permission to perform some action on this Entity
  * @param {User} user           The User requesting creation
  * @param {string} action       The attempted action
  * @return {boolean}            Does the User have permission?
  */
  can(user, action) {
    ...
  }
}

Subclass:

class User extends Entity {
  ...

  /**
   * Test whether the User is able to perform a certain permission action. Game Master users are always allowed to
   * perform every action, regardless of permissions.
   *
   * @param {string} permission     The action to test
   * @return {boolean}              Does the user have the ability to perform this action?
   */
   can(permission) {
     ...
   }
}

How can I faithfully represent overridden methods like the above without tsc pointing out the obvious? Or am I going to have to "lie" in some way and misrepresent the relationship between Entity and User?

2

There are 2 answers

2
spender On

You can create a type that erases the can property from the base Entity type, then assign Entity to a variable of that type.

Now you can create a new class derived from this "class"-reference variable.

This breaks polymorphism (as did the original developer). It's awful. Don't do this. Bite the bullet and refactor your mess.

class Entity {
    can(user: string, action: string) {
        console.log(user, action)
    }
}

type PartialEntity = new () => { [P in Exclude<keyof Entity, 'can'>]: Entity[P] }

const EntityNoCan: PartialEntity = Entity;

class User extends EntityNoCan {
    can(permission: number) {
        console.log(permission)
    }
}
0
Richard Simões On

It seems a // @ts-ignore is really the only option here. The other proposed solution isn't applicable to type declarations.