在TypeScript中扩展Kendo UI窗口

我正在尝试扩展Telerik的一个Kendo UI类,即 Window 小部件 . 我正在使用TypeScript 2.2.2 .

供您参考,这就是 Window 的定义 . 我只包括与此问题相关的部分 .

class Window extends kendo.ui.Widget {
    static extend(proto: Object): Window;
    constructor(element: Element, options?: WindowOptions);
    content(): string;
    content(content?: string): kendo.ui.Window;
    content(content?: JQuery): kendo.ui.Window;
}

我想覆盖 content(string) 方法 . 我使用this example作为我的代码的基础 . 所以我有以下内容:

export class AngularizedWindow extends kendo.ui.Window {
    constructor(element: Element, options?: kendo.ui.WindowOptions) {
        super(element, options);
    }

    content(content?: string) : kendo.ui.Window {
        console.log("Setting content");
        return super.content(content);
    }
}

但是,它给了我这个错误:

ts \ widgets \ AngularizedWindow.ts(2,18):错误TS2415:类'AngularizedWindow'错误地扩展了基类'Window' . property '内容'的类型是不相容的 . 类型'(内容?:字符串)=>窗口'不能分配给类型'{():string; (内容?:字符串):窗口; (内容?:JQuery):窗口; }” . “窗口”类型不能指定为“字符串”类型 .

我做错了什么?我不明白如何解释这个错误 .

回答(1)

2 years ago

正确格式化时,错误消息中的“不可分配”类型是

{ 
   (): string; 
   (content?: string): Window; 
   (content?: JQuery): Window; 
}

这是一个有3个所谓的callable signatures的类型,它描述了可以用以下三种方式之一调用的东西:

  • 没有参数,返回一个字符串

  • 带有可选的字符串参数,返回Window

  • 带有可选的JQuery参数,返回Window

's how typescript represents function overloading - it'是在kendo Window 中声明的 content 的实际类型,因为它有3个重载变体:

content(): string;
content(content?: string): kendo.ui.Window;
content(content?: JQuery): kendo.ui.Window;

Javascript没有函数重载,因此打字稿尽可能地模拟它,并且当你使用重载方法时它有用 .

但是,当您实现(或重写)重载方法时,typescript没有任何帮助 . 您只能有一个实现,它必须在运行时处理所有可能的参数组合 . 因此,您的扩展类必须重复 content() 的所有重载声明,并提供一个实现,与所有声明的变体兼容并能够处理所有这些,如下例所示:https://www.typescriptlang.org/docs/handbook/functions.html#overloads

我没有使用Kendo UI的经验,因此我根据有问题的代码编写了最小的示例,该代码使用typescript 2.3编译并在node.js中运行:

base.ts

export class Widget {}
export class Element {}
export class WindowOptions {}
export class JQuery {}

export namespace kendo {
    export namespace ui {

        export class Window extends Widget {
            static extend(proto: Object): Window {return null}
            constructor(element: Element, options?: WindowOptions) { super() }
            content(): string;
            content(content?: string): Window;
            content(content?: JQuery): Window;
            content(content?: string | JQuery): Window | string {
                return null;
            }
        }

    }
}

d.ts

import { WindowOptions, JQuery, kendo } from './base';

export class AngularizedWindow extends kendo.ui.Window {
    constructor(element: Element, options?: WindowOptions) {
        super(element, options);
    }

    content(): string;
    content(content?: string): kendo.ui.Window;
    content(content?: JQuery): kendo.ui.Window;

    content(content?: string | JQuery) : kendo.ui.Window | string {
        if (typeof content === 'undefined') {
            console.log('implementation 1');
            return super.content();

        } if (typeof content === 'string') {
            console.log('implementation 2');
            return super.content(content);


        } else { // ought to be jQuery
            console.log('implementation 3');
            return super.content(content);
        }
    }
}

let a = new AngularizedWindow(null);

a.content();
a.content('b');
a.content({});

编译并运行

./node_modules/.bin/tsc base.ts d.ts
 node d.js

它打印

implementation 1
implementation 2
implementation 3

现在,当您查看示例代码时,它引出了一个问题: content() 确实需要所有这些重载声明吗?它看起来像实现,采用union类型并返回union类型,足以处理所有用例 .

但是,没有重载,此代码不会编译:

let s: string = a.content();

错误:

d.ts(32,5): error TS2322: Type 'string | Window' is not assignable to type 'string'.
  Type 'Window' is not assignable to type 'string'.

因此,重载允许描述参数类型和返回类型之间的关系 . 但是,实现中的编译器不会强制执行该关系 . 如上所述,重载引入的额外复杂性是否值得,值得商榷,如_1309173中所述:

实际上,JavaScript没有函数重载,一般来说我建议人们根本不使用重载 . 继续使用参数上的联合类型,但如果您有多个不同的行为入口点,或者根据输入而返回不同的返回类型,请使用两个不同的函数!它最终使呼叫者更清楚,更容易编写 .