为什么TypeScript在实现泛型接口时无法推断函数参数的类型?

为什么TypeScript在实现泛型接口时无法推断函数参数的类型?,typescript,generics,Typescript,Generics,我正在Visual Studio 2015中编写TypeScript,安装了2.3.3.0版语言服务扩展。我在项目的tsconfig.json中将noImplicitAny参数设置为true 给出以下简单的示例代码: interface ITransformer<TInput, TOutput> { transform(input: TInput): TOutput; } class Input { name: string; } class Output {

我正在Visual Studio 2015中编写TypeScript,安装了2.3.3.0版语言服务扩展。我在项目的
tsconfig.json
中将
noImplicitAny
参数设置为
true

给出以下简单的示例代码:

interface ITransformer<TInput, TOutput> {
    transform(input: TInput): TOutput;
}

class Input {
    name: string;
}

class Output {
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

class Test {
    name: string;
}

class Transformer implements ITransformer<Input, Output> {
    transform = (input) => new Output(input.name);
}
但我的问题是,我为什么要这么做?在我看来,该参数的类型应该从接口实现中的
TInput
类型推断出来(在这种情况下,
Input

更令人担忧的是,我可以很高兴地做到这一点:

class Transformer implements ITransformer<Input, Output> {
    transform = (input: Test) => new Test();
}
类转换器实现ITransformer{
转换=(输入:Test)=>newtest();
}
它接受并返回两个类型参数都未引用的完全不同的类型,编译器似乎对此没有问题


来自C#背景,这似乎是错误的。我缺少什么?

在实现
ITransformer
接口时,您可以使用不同的签名覆盖
转换,例如:

class Transformer implements ITransformer<Input, Output> {
    transform(input: string): Output;
    transform(input: number): Output;
    transform(input: Input): Output;
    transform(input: any): Output {
        // ...
    }
}
至于为什么会这样做:

transform = (input: Test) => new Test();
这是因为和这两种类型具有相同的结构(
{name:string}
)。尝试仅向其中一个属性添加另一个属性,您将得到一个错误

我想指出的最后一点是,您定义类方法的方式实际上不会创建方法,而只是创建具有函数类型的成员:

class Test {
    method1() { }

    method2 = () => {}
}
这里只有
method1
才是真正的方法,而
method2
只是构造函数中分配的一个属性,它不会成为原型的一部分,如果扩展类,您将无法重写它

以下是此代码的编译版本:

var Test = (function () {
    function Test() {
        this.method2 = function () { };
    }
    Test.prototype.method1 = function () { };
    return Test;
}());

这种方法很好,正在使用中,但请注意。

谢谢,现在一切都变得更有意义了!因此,让我看看我是否理解了结构子类型部分:尽管可以使用与接口签名中指定的参数类型不同的参数类型从接口实现
transform
方法,只有当替代类型与接口签名类型具有完全相同的属性时,它才会实际编译。在这种情况下,不同对象的属性签名在编译的JavaScript中是相同的,因此不会发生运行时错误。我说的对吗?你把两个不同的问题混在一起了。只要其中一个签名与接口中的签名匹配,就可以为接口中声明的方法使用不同的重载。至于结构子类型问题,编译器是如何比较类型的。这两种类型只要在属性上匹配就相同。因此,在运行时,属性(及其类型)是相同的,因此不会导致任何错误。我的注释是在假设实现类中只有一个方法重载的情况下编写的(如我的示例所示),但我现在明白了您的意思。无论如何,你的评论的最后一部分基本上证实了我的最后一部分,所以谢谢你帮助我理解。
class Test {
    method1() { }

    method2 = () => {}
}
var Test = (function () {
    function Test() {
        this.method2 = function () { };
    }
    Test.prototype.method1 = function () { };
    return Test;
}());