TypeScript:实现通用接口

3
考虑以下通用接口:

interface Extractor<T> {
  extractCandidate(): T;
  process(candidate: T): T;
}

概念上,每个Extractor实现负责从某个数据源中提取特定类型的对象。 Extractor的使用者可以使用extractCandidate()提取候选对象,或者在给定任何候选对象的情况下,使用process()对其进行进一步处理以获得更精细的对象版本。

现在,假设我为类MyClass实现了一个Extractor,如下所示:

class MyClass {
  a: string;
  b: string;
}

class MyExtractor implements Extractor<MyClass> {
  extractCandidate() {
    //Some dummy logic
    return new MyClass();
  }
  process(candidate) {
    //Some dummy logic
    let res = new MyClass();
    res.a = candidate.a;
    return res;
  }
}

现在假设我实例化了MyExtractor并使用它:
let myExtractor = new MyExtractor();
let processed = myExtractor.process('blah');
问题 1: 为什么这不会生成编译时错误?根据 Extractor 接口的定义,我期望编译器不允许我使用除了 MyClass 实例或者结构兼容的实例调用 myExtractor.process()

问题 2: 我该如何强制执行所需的行为?我只需要断言 MyExtractor.process()candidate 参数是 MyClass 类型吗?

我怀疑这与 TypeScript 的结构类型系统有关,但在阅读了 some related questionsFAQ 后,我仍然不确定它如何具体应用于此处。

我的Typescript版本是2.1.4。

1个回答

4
您发布的 MyExtractor 代码中,process 方法的签名如下:
process(candidate: any): MyClass

那是因为您没有为candidate指定类型,所以默认为any
编译器不会发出警告,因为它满足candidate: T(因为any可以是T)。

如果您将代码更改为:

process(candidate: MyClass) {
    ...
}

然后针对以下内容:
let processed = myExtractor.process('blah');

您将会得到:

类型为“"blah"”的参数无法分配给类型“MyClass”的参数

您可以通过使用--noImplicitAny标志来避免这种情况,这将导致编译器抱怨:
process(candidate) {
    ...
}

提示:

参数“candidate”的类型未指定


编辑

该参数的类型不是“任何其他类型”,而是允许为any(这是默认值),例如,这样做的好处之一是用于重载函数时:

process(candidate: string): MyClass;
process(candidate: MyClass): MyClass;
process(candidate: any) {
    ...
}

1
好的,但是我没有在candidate上指定类型约束吗?在MyExtractor中,TMyClass,那么为什么candidate可以是其他任何类型?实际上,如果我断言candidate的类型是string并返回一个空字符串,我会得到我期望的错误:"类型'(candidate: string) => string'不能赋值给类型'(candidate: MyClass) => MyClass'。参数'candidate'和'candidate'的类型不兼容。类型'MyClass'不能赋值给类型'string'。" - ethan.roday
请检查我的修订答案。 - Nitzan Tomer
1
如果TS能够在这种情况下上下文地将process(candidate)类型化为process(candidate: MyClass): MyClass,那就太好了。我曾经遇到过类似的困惑,因为React组件定义指定了成员的类型,但实际上你可以使用不正确的类型进行实现,最终还是要在实现中给它正确的类型。 - Aaron Beall
1
@Aaron 接口声明方法需要一个 MyClass 参数并不意味着实现不能处理其他东西,这就是编译器不会做出这种假设的原因。为了避免这个“问题”,只需使用 noImplicitAny,然后你就会知道何时/在哪里有这些情况。 - Nitzan Tomer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接