【问题标题】:Interfaces with construct signatures not type checking具有构造签名而不是类型检查的接口
【发布时间】:2012-10-08 18:27:53
【问题描述】:

我一直在使用 TypeScript 中带有构造签名的接口,当以下类型检查失败时,我有点困惑:

class Foo {
    constructor () {
    }
}

interface Bar {
    new(): Bar;
}

function Baz(C : Bar) {
    return new C()
}

var o = Baz(Foo);

类型错误是:

提供的参数与调用目标的任何签名都不匹配: 'new() => Foo' 和 'Bar' 类型的构造签名是 不兼容:类型“Bar”需要构造签名,但类型 'Foo' 缺少一个 (C: Bar) => Bar

Foo 的构造函数的类型是 () => Foo,这就是我认为 Bar 所说的。我在这里遗漏了什么吗?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这是您的代码的更新版本,有细微的变化。

    我们使用我们期望出现的任何函数和变量来定义Bar 接口。

    接下来,我们用NewableBar 接口扩展Bar 接口。这只是定义了一个返回Bar的构造函数。

    因为Foo 实现了Bar 并且有一个构造函数,而Baz 需要一个NewableBar,所以一切都被检查了。

    这比any 更详细一点 - 但可以为您提供所需的检查。

    interface Bar {
    
    }
    
    interface NewableBar extends Bar {
        new();
    }
    
    class Foo implements Bar {
        constructor () {
    
        }
    }
    
    function Baz(C : NewableBar) {
        return new C()
    }
    
    var o = Baz(Foo);
    

    【讨论】:

    • 你使用超级接口代替any(你可以省略implementsextends,顺便说一句)。没关系,我想,而且比any 更好,但有一件事仍然困扰着我:你为什么不能让NewableBar::new() 返回NewableBar?为什么它必须是比NewableBar更通用的类型(显然至少与Foo 一样通用)?
    • @AdrianLang 我将显式继承和实现明确地放入其中,但您是对的 - 如果一切都匹配,TypeScript 会很高兴地推断出这些。尝试将方法上的 new() 签名视为特定类型时存在解释问题。
    • 但是,每当您尝试向 Foo 添加任何内容时都会中断。编辑:对不起,我做错了。我的构造签名应该是:new(... _ : any[]) : Bar;
    • 这真的是因为未初始化的Foo 没有初始化时创建的函数,所以Foo 不能替代new Foo()。我将根据我认为您的用途在不同的答案中发布替代解决方案。
    【解决方案2】:

    问题(至少从 TypeScript 编译器的角度来看)是Barnew 方法的签名。如果您将Bar 的定义替换为以下内容,

    interface Bar {
      new (): any;
    }
    

    它有效。你还不如使用new (): Foo,只使用Bar,因为返回值不起作用。

    【讨论】:

      【解决方案3】:

      我想我知道你在做什么,我认为你需要一种微妙的不同方法。

      这个例子说明了以下内容:

      • Baz 必须传递一个新的项目。
      • Baz 返回一个酒吧
      • 并非所有 Bar 都需要是可更新的,只有那些传递给 Baz 的才需要更新

      示例如下:

      interface Bar {
          sayHello(name: string): void;
      }
      
      interface Newable {
          new();
      }
      
      class Foo implements Bar {
          constructor () {
      
          }
      
          sayHello(name: string) {
              window.alert('Hello ' + name);
          }
      }
      
      function Baz(C : Newable) {
          return <Bar> new C()
      }
      
      var o = Baz(Foo);
      o.sayHello('Bob');
      

      这种方法的唯一危险是您可能会将一些不是 Bar 的新事物传递给 Baz 函数。当您通过从参数创建对象来使用动态功能时,这在很大程度上是不可避免的,除非您愿意传入预初始化的对象,在这种情况下,Baz 会很乐意接受 Bar 而不是 newable。

      【讨论】:

      • 是的,这就是我想要实现的目标。除了我想BarNewable 成为一个接口。谢谢。
      【解决方案4】:

      我知道这是一个老问题,但我今天需要类似的东西并且遇到了帖子。经过反复试验,我想出了以下解决方案:

      interface Bar { 
          sayHello(name: string);
      }
      
      class Foo implements Bar {
          sayHello(name: string) {
              window.alert("Hello " + name);
          }
      }
      
      function Baz(c: new() => Bar) {
          return new C();
      }
      
      var o = Baz(Foo);
      o.sayHello("Bob");
      

      基本上接口只能定义对象实例的契约,因此必须在将调用构造函数的函数上完成要求存在某个构造函数的操作。这种方法也可以很好地与泛型配合使用:

      function Baz<T extends Bar>(c: new() => T) {
          return new c();
      }
      
      var o = Baz(Foo);
      

      在上面的示例中,变量“o”将被推断为 Foo 类型。

      【讨论】:

        【解决方案5】:

        确实有一个简单的答案。看看你的Bar 界面:

        interface Bar {
            new(): Bar;
        }
        

        假设您已经设法创建了一些实现此接口的Magic 类。

        class Magic implements Bar {
            // ...
        }
        

        这意味着Magic 的任何实例都必须遵守Bar 的规范。因此,假设您有一个名为 magicMagic 实例。

        由于magic 遵循Bar 的规范,magic 必须是可更新的,这意味着以下必须有效:

        const foo = new magic() // lowercase m!
        

        此外,该调用的结果,即foo,本身必须遵守Bar。这意味着以下内容也必须有效:

        const zzz = new foo();
        

        然后很明显,以下内容也必须有效:

        new (new (new (new (new (new (new (new Magic())))))))
        

        很明显,您的代码不满足此条件。因此,您的代码不会键入检查。

        【讨论】:

          猜你喜欢
          • 2017-08-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-04
          • 1970-01-01
          • 2010-11-11
          • 2013-11-06
          相关资源
          最近更新 更多