TypeScript 编译器应该对子类构造函数的隐式返回值发出警告,如果它不满足子类类型,

ufj5ltwl  于 6个月前  发布在  TypeScript
关注(0)|答案(9)|浏览(73)

TypeScript 版本: 2.1.5

自从 TypeScript 2.1(由于 #7574 )以来,构造函数调用的返回值是超类构造函数的返回值。因此,

class Child extends Base {}

会被编译成

function Child() {
     return _super !== null && _super.apply(this, arguments) || this;
}

实际行为:

实际上的问题是,尽管示例 ( child ) 不满足其类型 Child ,但 Child (参见下面的示例)的实现将不会出错,因为 someMethod 并不存在于 child 上。

class Base {
    hello = 'base';
    constructor() {
        return {
            hello: 'world'
        };
    }
}

class Child extends Base {  // <-- compiler does not complain
    someMethod() {}
}

const child = new Child();
child.someMethod();  // <-- compiler does NOT complain, despite of "someMethod" does not exist on {hello: 'world'}

预期行为:

如果子类构造函数的隐式返回值不是子类的类型,编译器应该对此发出警告。因此,要么像这样强制为子类构造函数显式定义返回值...

class Child extends Base { 

   constructor() {
     super();
     return { 
       someMethod() {},
       /* ... inherited members */
     }
   }
   someMethod() {}
}

要么需要扩展返回值( const base = super() )以满足类型 Child ,或者所有子类成员都必须是可选的。

w46czmvw

w46czmvw1#

实际问题是,尽管示例(子对象)不满足其类型Child,但Child(见下例)的实现仍然可以成功编译,因为child上不存在someMethod方法。

但问题不在Child,而在BaseBase中,它们都能正确报告错误。那么请求在Child中也报告另一个错误是否合理?

vqlkdk9b

vqlkdk9b2#

但是问题不在Child,而是在Base,Base正确地报告了错误。那么请求在Child中也报告另一个错误吗?
啊,抱歉,表述不够清楚。这个问题不是关于Base类缺少someBaseMethod成员,而是关于Child示例缺少someMethod成员。我已经调整了示例并删除了someBaseMethod。希望这能澄清实际的问题。

6ie5vjzr

6ie5vjzr3#

我认为这是一个我们理解的问题,但由于建模问题存在的困难以及我们预期的相对罕见性,我们决定采取宽松的态度。
我不认为我知道描述这种行为的具体机制,除了在构造签名上进行新的编码。作为一个例子,像这样:

interface FooStatic {
    new(): explicit return Foo
}

但即使是这样,它也面临着一个问题,即它无法传达返回对象的原型是否适当地进行了调整。

yiytaume

yiytaume4#

CC @justinfagnani for any ideas

anauzrmj

anauzrmj5#

但是这还不足以构成编译错误吗?因为tsc在这种情况下已经抛出了异常:

class Foo {
    bar: string;
    baz: number;
    constructor() {
        return {
            bar: 'hey', // <-- baz is missing
        };
    }
}

我希望当出现描述的问题时,编译器也会抛出异常。

ep6jt1vc

ep6jt1vc6#

我认为问题在于构造函数被类型化以返回其包含类的示例类型,而不是 this 类型。返回 this 的方法似乎可以正常工作:
(忽略构造函数中的运行时无限循环 :) )

export class A {
  constructor() {
    return new A(); // no error
  }
  foo(): this {
    return new A(); // error: Type 'A' is not assignable to type 'this'.
  }
}

如果构造函数必须返回一个 this ,那么它将迫使作者要么进行类型转换并说“相信我”,或者做更正确的事情:

export class A {
  constructor() {
    return Object.create(new.target.prototype);
  }
}

^ 我看到 Object.create 已经可以正确地推断出像 Object.create(A.prototype) 这样的用法的类型,但我不确定让它理解 Object.create(new.target.prototype) 具有 this 类型的难度有多大。

efzxgjgh

efzxgjgh7#

我想要添加一些更现实的使用案例,用于返回除 this 之外的其他内容,以查看它如何进行类型检查:

  1. 单例/缓存示例。这里是一些尝试使单例与子类化一起工作的方法,假设子类通过不进行双重初始化来合作。
const cache = new WeakMap();
export class A {
  constructor() {
    const cached = cache.get(new.target);
    if (cached != null) {
      return cached as this;
    }
    cache.set(new.target, this);
  }
}
  1. "构造函数调用技巧"。这是HTMLElement在解析过程中升级自定义元素的内部方法。这基本上是在解决你不能 Function.call 一个类构造函数的问题。
zu0ti5jz

zu0ti5jz8#

我看到2.2rc支持new.target,但它没有被推断为this类型。已提交#13849

50pmv0ei

50pmv0ei9#

@DanielRosenwasser ,你觉得我的诊断在这里是否正确?当从构造函数返回时,问题是否应该更改为要求this类型?

相关问题