Richard Simões
Richard Simões

Reputation: 12813

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

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?

Upvotes: 0

Views: 219

Answers (2)

Richard Simões
Richard Simões

Reputation: 12813

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

Upvotes: 0

spender
spender

Reputation: 120548

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)
    }
}

Upvotes: 2

Related Questions