Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: override and fix typing definitions for packages

I'm using the React victory library for charts, and I'm using TypeScript. I have added the @types/victory package to my project, but unfortunately, it's missing a bunch of key definitions that I need. For some interfaces, it was missing a couple of properties, so (using this answer), I created a custom file to add those properties to the interfaces.

But, now I have a problem: the interface defined for events has a property named eventHandlers defined, but again it's missing an important definition, how it's defined:

export interface EventPropTypeInterface<TTarget, TEventKey> {
  /* other properties */,
  eventHandlers: {
    [key: string]:
      { (event: React.SyntheticEvent<any>): EventCallbackInterface<TTarget, TEventKey> } |
      { (event: React.SyntheticEvent<any>): EventCallbackInterface<TTarget, TEventKey>[] }
  }

But, the problem is that the function should allow accepting a second parameter, example (I'm not sure about the type of the second argument, so I have any):

{ (event: React.SyntheticEvent<any>, dataPoint: any): EventCallbackInterface<TTarget, TEventKey> }

So, I tried to include this property in my own definition:

declare module "victory" {
  export interface EventPropTypeInterface<TTarget, TEventKey> {
  eventHandlers: {
    [key: string]:
      { (event: React.SyntheticEvent<any>, dataPoint: any): EventCallbackInterface<TTarget, TEventKey> } |
      { (event: React.SyntheticEvent<any>, dataPoint: any): EventCallbackInterface<TTarget, TEventKey>[] }
  }
}

But now, TypeScript is complaining about the index.d.ts in the @typings directory, saying that eventHandlers must be of the type I have defined.

Is there a way to use the provided type definition file and somehow "augment" it with my own definition similar to what I did with other interfaces when just adding new properties?

like image 605
Farzad Avatar asked Feb 13 '18 16:02

Farzad


1 Answers

This can work. The problem is that the types were not defined with extensibility in mind.

Object literal types, such as {x: string} are great in many situations, but not for complex nested structures.

The reason is that these types, similar to type aliases, are not subject to the declaration merging needed to enable extensibility.

If the types of the possible values of the eventHandlers object were declared as an interface it could be extended with ease by simply adding additional overloads.

The following, self contained example shows how extensibility is quite easy when the types are refactored to use an interface declaration for the value type of eventHandlers:

declare module "victory" {
    interface EventHandler<TTarget, TEventKey> {
        (event: React.SyntheticEvent<any>): EventCallbackInterface<TTarget, TEventKey>
        (event: React.SyntheticEvent<any>): EventCallbackInterface<TTarget, TEventKey>[]
    }

    export interface EventPropTypeInterface<TTarget, TEventKey> {
        eventHandlers: {
            [key: string]: EventHandler<TTarget, TEventKey>

        }
    }
}
declare module "victory" {
    // this interface merges with the original, declaring additional overloads of the all signature
    interface EventHandler<TTarget, TEventKey> {
        // declare overloads
        (event: React.SyntheticEvent<any>, dataPoint: any): EventCallbackInterface<TTarget, TEventKey>
        (event: React.SyntheticEvent<any>, dataPoint: any): EventCallbackInterface<TTarget, TEventKey>[]
    }
}

type EventCallbackInterface<T, U> = {}
declare namespace React {
    export type SyntheticEvent<T> = {};
}

Test:

import victory = require("victory");

declare const handlerProps: victory.EventHandler<{}, {}>;

handlerProps({}, { x: 1, y: 2 }); // works fine

Here is a link to it in action

I strongly recommend that you submit a Pull Request to https://github.com/DefinitelyTyped/DefinitelyTyped that modifies npm:@types/victory to use an interface in this manner to enable such extensibility.

like image 186
Aluan Haddad Avatar answered Sep 27 '22 20:09

Aluan Haddad