Как расширить интерфейс, объявленный во внешней библиотеке d.ts?

Я установил определения knockout, используя задокументированный метод, подобный этому.

npm install @types/knockout

Он прекрасно работает, я могу импортировать его куда угодно.

import * as ko from "knockout";

Однако я застрял в расширении интерфейса KnockoutStatic некоторыми пользовательскими вещами. Я пытаюсь перенести огромное приложение TS на основе <reference ... /> и namespace для использования модулей. Раньше я легко объявлял интерфейс расширения где угодно, и объявления объединялись. Допустим, мое расширение выглядит так.

interface KnockoutStatic {
  doSomething(): void;
}

Я попытался создать файл KnockoutExtensions.d.ts, где я объявил его так.

import "knockout";

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

Но когда я куда-то импортирую и knockout, и свой добавочный номер, TS все еще не может разрешить вызовы doSomething.

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

ko.doSomething(); // error

Каков правильный метод расширения интерфейсов библиотек с помощью TypeScript 2.0 и новой подсистемы d.ts?

Я использую Visual Studio 2015 Update 3 с установленным TypeScript 2.0.


person Zoltán Tamási    schedule 04.10.2016    source источник


Ответы (5)


Вы можете легко расширить «knockout» или любое другое пространство имен TypeScript.

Пример: создайте файл Knockout-extension.d.ts

/// <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;
  }
}

Примечание: убедитесь, что этот файл подхвачен компилятором TypeScript.

Используйте вновь определенные типы из расширенного модуля.

// 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;

Дополнительную информацию о модулях и пространствах имен можно найти в документации по TypeScript в разделах Модули и Пространства имен и их использование вместе.

Ваше здоровье!

person S.Klechkovski    schedule 29.06.2017

Я обнаружил, что winston имеет ту же проблему, используя синтаксис export =. Я нашел эту страницу полезной, когда она показала, что react сделал то же самое: https://www.credera.com/blog/technology-solutions/typescript-adding-custom-type-definitions-for-existing-библиотеки/.

Решение, которое они рекомендовали, которое, как мне показалось, сработало, таково:

import 'react';

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

Простой импорт модуля, а затем его объявление позволяет любым интерфейсам в этом блоке объявлений расширять существующие, а в других частях вашего кода вы можете продолжать использовать интерфейс, как обычно; то есть вы все равно импортируете react или winston или knockout и увидите эти новые элементы интерфейса. Вам не нужно начинать ссылаться на пользовательский интерфейс или что-то в этом роде.

person cjbarth    schedule 02.10.2019

Проблема в том, что файл ввода knockout использует синтаксис export =, и он не является «дружественным к дополнению». См. это в качестве справки.

Самым простым решением для меня было обернуть расширения в declare global { }, поскольку файл ввода knockout объявляет все в глобальной области видимости.

declare global {
  interface KnockoutStatic {
    doSomething(): void;
  }
}
person Zoltán Tamási    schedule 04.10.2016

Вам нужно создать свой интерфейс вне вашего модуля. не объявляйте его при экспорте.

module example { //...do stuff }

interface KnockoutStatic { doSomething(): void; }

Вы можете добавить свой файл, в который вы добавляете расширения интерфейса, чтобы он оставался чистым.

person seven    schedule 04.10.2016
comment
Это не сработало, это сработало только тогда, когда я использовал не модули, а чистые пространства имен. - person Zoltán Tamási; 04.10.2016

Этот код работает для меня

// observable.ts
export class Observable<T> {
  // ... implementation left as an exercise for the reader ...
}

// map.ts
import { Observable } from "./observable";
declare module "./observable" {
  interface Observable<T> {
    map<U>(f: (x: T) => U): Observable<U>;
  }
}
Observable.prototype.map = function (f) {
  // ... another exercise for the reader
};

// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map((x) => x.toFixed());
person surinder singh    schedule 21.01.2021