Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend an interface declared in an external library d.ts?

I installed the knockout definitions using the documented method like this.

npm install @types/knockout

It works nicely, I could import it like this anywhere.

import * as ko from "knockout";

However, I'm stuck with extending KnockoutStatic interface with some custom stuff. I'm trying to migrate a <reference ... /> and namespace based huge TS application to use modules. Before, I easily declared the extension interface anywhere and the declarations got merged. Let's say my extension looks like this.

interface KnockoutStatic {
  doSomething(): void;
}

I tried to create a KnockoutExtensions.d.ts file where I declared it like this.

import "knockout";

declare module "knockout" {
  export interface KnockoutStatic {
    doSomething(): void;
  }
}

But when I import both knockout and my extension somewhere, TS still cannot resolve the doSomething calls.

import * as ko from "knockout";
import "./KnockoutExtensions";

ko.doSomething(); // error

What is the proper method of extending library interfaces using TypeScript 2.0 and the new d.ts subsystem?

I'm using Visual Studio 2015 Update 3 with TypeScript 2.0 installed.

like image 667
Zoltán Tamási Avatar asked Oct 04 '16 13:10

Zoltán Tamási


People also ask

How do you extend an interface in TypeScript?

Extending Interfaces in TypeScript # Use the extends keyword to extend interfaces in TypeScript, e.g. interface Dog extends Animal {age: number;} . The extends keyword allows us to copy the members from other named types and add new members to the final, more generic interface. Copied!

Can an interface extend a class in TypeScript?

In TypeScript, interfaces can also extend classes, but only in a way that involves inheritance. When an interface extends a class, the interface includes all class members (public and private), but without the class' implementations.

What does it mean to extend an interface?

Extending InterfacesAn interface can extend another interface in the same way that a class can extend another class. The extends keyword is used to extend an interface, and the child interface inherits the methods of the parent interface.


2 Answers

You can easily extend the 'knockout' or any other TypeScript namespace.

Example: create knockout-extension.d.ts file

/// <reference path="<path-to-typings-dir>/knockout/index.d.ts" />

declare module 'knockout' {

  export interface CustomType {

    customField: string;

    customMethod(arg1: number, arg2: boolean): boolean;
  }

  namespace customNamespace {

    export interface AnotherCustomType {
      customField1: string;
      customField2: boolean;
    }
  }

  // NOTE: extending existing interface
  export interface KnockoutStatic {
    customMethod(): void;
  }
}

Note: ensure that this file is picked-up by the TypeScript compiler.

Use the newly defined types from the extended module.

// one way
import { CustomType } from 'knockout';

const foo: CustomType;

// second way
import * as kc from 'knockout';

const foo: kc.CustomType;
const bar: kc.customNamespace.AnotherCustomType;

For more info on modules and namespaces you can check TypeScript documentation on Modules and Namespaces and using them together.

Cheers!

like image 100
S.Klechkovski Avatar answered Nov 10 '22 14:11

S.Klechkovski


I found that winston has the same problem, using the export = syntax. I found this page helpful when it showed that react did the same thing: https://www.credera.com/blog/technology-solutions/typescript-adding-custom-type-definitions-for-existing-libraries/.

The solution they recommended, which I found worked is this:

import 'react';

declare module 'react' {
    interface OlHTMLAttributes<T> {
        type?: "1" | "a" | "A" | "i" | "I";
    }
}

Simply importing the module, and then declaring it allows any interfaces in this declaration block to extend the existing ones and in other parts of you code you can go on using the interface as you normally would; i.e. you would still import react or winston or knockout and you'd see these new interface members. You don't have to start referencing a custom interface or anything like that.

like image 42
cjbarth Avatar answered Nov 10 '22 15:11

cjbarth