【问题标题】:How can I extend an existing interface when the definitions require an import statement?当定义需要导入语句时,如何扩展现有接口?
【发布时间】:2016-12-11 16:35:40
【问题描述】:

我创建了一个自定义 Knockout 扩展程序,但在扩展由 Knockout 的定义文件提供的现有接口时遇到了问题。

扩展器/Numeric.ts

import * as ko from "knockout";

function Extender(target: KnockoutObservable<number>, options: IOptions = {}): KnockoutObservable<number> {
    // ...
};

interface IOptions {
    // ...
}

export {Extender as NumericExtender, IOptions as INumericExtenderOptions}

Boot.ts

import * as ko from "knockout";
import {NumericExtender} from "./Extenders/Numeric";

class Boot {    
    public constructor() {
        ko.extenders.numeric = NumericExtender;
    }
}

为了让编译器了解ko.extenders.numeric,我需要扩展现有的接口:

interface KnockoutExtenders {
    numeric(target: KnockoutObservable<number>, options?: INumericExtenderOptions): KnockoutObservable<number>;
}

现在我遇到了麻烦。为了访问INumericExtenderOptions,我需要一个import 声明:

import {INumericExtenderOptions} from "./Extenders/Numeric";

但是当添加import语句时,文件被认为是一个模块,which makes it impossible to extend an existing interface。

有没有办法做到这一点,或者我需要将IOptions 移动到定义文件以避免import?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    您似乎正在使用淘汰声明文件的全球版本。我认为不可能从模块声明文件中扩展在全局范围内定义的接口。有几种解决方案:

    • 我认为最简单的解决方案是将您的界面也放入全局命名空间。对于 IOptions,这变为:

      // index.d.ts
      interface IOptions {
        // ¯\_(ツ)_/¯
      }
      
      interface KnockoutExtenders {
        numeric(target: KnockoutObservable<number>, options?: IOptions): KnockoutObservable<number>;
      }
      

      现在您可以在任何地方使用数字函数访问 IOptions 和 KnockoutExtenders,因为声明文件仍然是全局的。

    • 另一种解决方案是引入模块版本(我认为这是我的首选解决方案,因为您不会使用所有淘汰类型污染全局命名空间)。在淘汰赛的情况下:typings install --save knockout。然后,您必须在需要时专门导入所需的类型。例如。你的 numeric.ts 变成了

      // src/numeric.ts
      import { Observable } from "knockout"
      
      export function Extender(target: Observable<number>, options: IOptions = {}): Observable<number> {
        // ¯\_(ツ)_/¯
      };
      
      export interface IOptions {
        // ¯\_(ツ)_/¯
      }
      

      然后您可以在另一个声明文件中扩充淘汰模块。例如:

      // index.d.ts
      import { Observable } from "knockout"
      import { IOptions } from './src/numeric'
      
      declare module "knockout" {
        interface Extenders {
          numeric(target: Observable<number>, options?: IOptions): Observable<number>
        }
      }
      

      那么您应该能够在应用程序的任何地方使用增强的扩展器接口:

      // src/boot.js
      import { extenders } from "knockout"
      import { Extender } from "./numeric"
      
      class Boot {
        public constructor() {
          extenders.numeric = Extender
        }
      }
      
    • 最终解决方案(可能是解决您的问题的最佳方法)是使用模块化声明文件,但要增强全局模块。您的 numeric.ts 保持不变,您的声明文件变为:

      import { IOptions } from './src/numeric'
      
      declare global {
        interface KnockoutExtenders {
          numeric(target: KnockoutObservable<number>, options?: IOptions): KnockoutObservable<number>
        }
      }
      

    欲了解更多信息,请查看page on declaration merging from the typescript handbook.

    【讨论】:

    • 设法找到一些时间来解决这个问题,我最终使用了第三种解决方案。目前没有可用的 Knockout 模块版本,因此第二种解决方案不适用于这种情况。
    • 很奇怪。 typings install knockout 肯定会安装一个模块淘汰声明文件。但也许这不是您想要的文件?
    • 我的意思是实际的库 :) 不是打字。除非我误解了一些事情并且有办法“模块化”一个库?
    • 和我一样,第三个解决方案很棒!正是我需要的。 (奇怪的是它没有在更多地方提到)
    猜你喜欢
    • 2018-08-22
    • 2015-06-21
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-18
    • 2014-11-02
    • 2017-04-29
    相关资源
    最近更新 更多