从非类继承ES6 / TS类

时间:2016-01-03 12:53:33

标签: javascript typescript ecmascript-6

鉴于该类是从非类扩展(包括但不限于函数),

function Fn() {} 

class Class extends Fn {
    constructor() {
        super();
    }
}

后果是什么?规格对此有何评论?

看起来Babel,Google V8和Mozilla Spidermonkey的当前实现都可以,而且TypeScript抛出

  

输入'()=> void'不是构造函数类型

如果这是一个有效的ES2015代码,那么在TypeScript中处理它的正确方法是什么?

4 个答案:

答案 0 :(得分:12)

TypeScript Part

到目前为止,spec表示extends克拉后面必须跟一个 TypeReference TypeReference必须采用A.B.C<TypeArgument>的形式,例如MyModule.MyContainer<MyItem>。所以,从语法上来说,你的代码是正确的。但这不是输入案例。

规范说BaseClass必须是有效的打字稿类。但是,规范已经过时,如here所述。现在TypeScript允许在extends子句中使用表达式,只要表达式计算到构造函数。该定义是基于实现的。你可以看到它here。简单地说,如果表达式实现new() {}接口,则表达式可以计为构造函数。

ES2015 Part

所以,你的问题是普通函数在TypeScript中不被认为是构造函数,这是有争议的,因为ES2015规范只要求对象有一个[[construct]]内部方法。虽然用户定义的函数对象确实有它。

ES2015运行时时需要BaseClass is a constructor。对象isConstructor,如果它有[[construct]] internal methd。规范说[[construct]]Function Object的内部方法。用户函数是Function Objects的实例,因此它们自然是构造函数。但builtin functionarrow function可能没有[[construct]]

例如,以下代码将抛出运行时TypeError,因为parseInt是内置函数且没有[[construct]]

new parseInt()
// TypeError: parseInt is not a constructor

来自ECMAScript

  

箭头函数就像内置函数一样缺乏.prototype和任何[[Construct]]内部方法。所以new(()=&gt; {})抛出一个TypeError,否则箭头就像函数一样:

根据经验,任何没有prototype的函数都不是new -

解决方法

简而言之,并非每个函数都是构造函数,TypeScript通过要求new() {}来捕获它。但是,用户定义的函数是构造函数。

要解决此问题,最简单的方法是将Fn声明为变量,然后将其强制转换为构造函数。

interface FnType {}
var Fn: {new(): FnType} = (function() {}) as any

class B extends Fn {}

推理不兼容性

DISCALIMER:我不是TypeScript的核心贡献者,但只是一个TS粉丝,他有几个与TS相关的副项目。所以这部分是我个人的猜测。

TypeScript是一个源自2012的项目,当时ES2015在昏暗的黑暗中仍然隐约可见。 TypeScript没有类语义的良好引用。

当时,TypeScript的main goal与ES3 / 5保持兼容。因此,new函数在TypeScript中是合法的,因为它在ES3 / 5中也是合法的。同时,TypeScript还旨在捕获编程错误。 extends函数可能是一个错误,因为函数可能不是一个合理的构造函数(比如,一个仅用于副作用的函数)。 ES3 / 5中甚至不存在extends!因此TypeScript可以自由定义自己对extends的使用,使extends必须与class变量配对。这使 TypeScript 更多 TypeSafe ,同时与JavaScript兼容。

现在,ES2015规范已经完成。 JavaScript还有extends个关键字!然后不兼容了。有efforts来解决不兼容问题。但是,仍然存在问题。由于内置函数,() => voidFunction类型不应该扩展能力,如上所述。以下代码将中断

var a: (x: string) => void = eval
new a('booom')

另一方面,如果在TypeScript中引入了ConstructorInterface并且每个函数都实现了它,那么就会出现向后不兼容性。以下代码现在编译,但不是在引入ConstructorInterface时编译

var a = function (s) {}
a = parseInt // compile error because parseInt is not assignable to constructor

当然,TS团队可以有一个平衡这两个选项的解决方案。但这不是一个高优先级。此外,如果 salsa ,TS驱动的JavaScript的代号完全实现。这个问题自然会得到解决。

答案 1 :(得分:4)

  

后果是什么?规格对此有何评论?

与扩展&#34; EcmaScript 5&#34;相同。类。你声明了一个构造函数,根本没有原型。您可以毫无问题地扩展它。

但对于TypeScript,function Fn() {}class Fn {}之间存在很大差异。两者的类型不同。

第一个是一个只返回任何函数的函数(TypeScript用() => void显示它)。第二个是构造函数。 TypeScript拒绝对非构造函数进行扩展。

如果有一天javascript拒绝这样做,它会破坏许多javascript代码。因为目前,function Fn() {}是在纯javascript中声明类的最常用方法。但从TypeScript的角度来看,这不是&#34;类型安全&#34;。

我认为TypeScript的唯一方法是使用类:

class Fn {} 

class Class extends Fn {
    constructor() {
        super();
    }
}

答案 2 :(得分:1)

我不确定我是否回答了你的问题,但我对如何通过TypeScript类扩展JS函数非常感兴趣,所以我尝试了以下内容:

fn.js (请注意.js扩展程序!)

function Fn() {
    console.log("2");
}

<强> c.ts

declare class Fn {}

class C extends Fn {
    constructor() {
        console.log("1");
        super();
        console.log("3");
    }
}

let c = new C();
c.sayHello();

然后我跑了:

$ tsc --target es5 c.ts | cat fn.js c.js | node  # or es6

,输出为:

1
2
3
Hello!

请注意,这不是生产用途的代码,而是在您没有时间将某些旧的JS文件转换为TypeScript时的解决方法。

如果我处于OP状态,我会尝试将Fn转换为类,因为它可以让团队中的其他人更轻松地编写代码。

答案 3 :(得分:1)

这与TypeScript interface to describe class

间接相关

虽然typeof一个类是function,但是在Typescript中没有Class(也没有Interface),它是所有类的超类。

然而,

函数都由Function

接口

我想与Javascript一致,Function应该是一个类,所有函数都应该是该类,所有类都应该扩展它...