从非类继承ES6/TS类
Inherit ES6/TS class from non-class
如果类是从非类(包括但不限于函数)扩展而来的,
function Fn() {}
class Class extends Fn {
constructor() {
super();
}
}
后果是什么?规格说明是什么?
看起来目前Babel、Google V8和Mozilla Spidermonkey的实现都可以,TypeScript抛出
类型"()=>void"不是构造函数类型
如果这是一个有效的ES2015代码,那么在TypeScript中处理它的正确方法是什么?
TypeScript部分
到目前为止,规范规定extends
子句后面必须跟一个TypeReference。TypeReference的形式必须是A.B.C<TypeArgument>
,就像MyModule.MyContainer<MyItem>
一样。因此,从语法上讲,您的代码是正确的。但这不是打字的情况。
规范规定BaseClass
必须是一个有效的typescript类。然而,正如这里所说,规范已经过时了。现在TypeScript允许在extends子句中使用表达式,只要表达式是根据构造函数计算的。这个定义是基于实现的。你可以在这里看到。简单地说,如果一个表达式实现了new() {}
接口,那么它就可以算作构造函数。
ES2015部分
因此,您的问题是普通函数在TypeScript中未被识别为构造函数,这是有争议的,因为ES2015规范只要求对象具有[[construct]]
内部方法。而用户定义的函数对象确实有它。
ES2015要求BaseClass
是运行时的构造函数。一个对象isConstructor
,如果它有[[construct]]
内部方法。规范中说[[construct]]
是函数对象的内部方法。用户函数是Function Objects
的实例,所以它们自然是构造函数。但是内置函数和箭头函数不能有[[construct]]
。
例如,以下代码将抛出运行时TypeError
,因为parseInt
是一个内置函数,没有[[construct]]
new parseInt()
// TypeError: parseInt is not a constructor
和来自ECMAScript
箭头函数和内置函数一样,都缺少.protype和任何[[Construct]]内部方法。因此new(()=>{})抛出一个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的主要目标是与ES3/5保持兼容。因此,new
调用函数在TypeScript中是合法的,因为它在ES3/5中也是合法的。同时,TypeScript还旨在捕获编程错误。extends
函数可能是一个错误,因为该函数可能不是一个合理的构造函数(例如,一个仅用于副作用的函数)。extends
甚至不存在于ES3/5中!因此TypeScript可以自由定义自己对extends
的使用,使得extends
必须与class
变量配对。这使得TypeScript在与JavaScript兼容的同时具有更多的TypeSafe。
现在,ES2015规范已经定稿。JavaScript还有一个extends
关键字!然后不兼容就来了。正在努力解决不兼容问题。然而,问题仍然存在。如上所述,由于内置函数,() => void
或Function
类型不应为扩展功能。以下代码将破坏
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的代号)已完全实现。这个问题自然会解决的。
后果是什么?规格说明是什么?
这与扩展"EcmaScript 5"类相同。您声明了一个构造函数,但根本没有原型。你可以毫无问题地扩展它。
但对于TypeScript,function Fn() {}
和class Fn {}
之间有很大的区别。两者不属于同一类型。
第一个只是一个不返回任何内容的函数(TypeScript用() => void
显示它)。第二个是构造函数。TypeScript拒绝对非构造函数函数进行扩展。
如果有一天javascript拒绝这样做,它将破坏许多javascript代码。因为目前,function Fn() {}
是用纯javascript声明类的最常用方法。但从TypeScript的角度来看,这不是"类型安全"的。
我认为TypeScript的唯一方法是使用一个类:
class Fn {}
class Class extends Fn {
constructor() {
super();
}
}
我不确定我是否回答了你的问题,但我对如何通过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
转换为一个类,因为这会使团队中的其他人更容易编写代码。
这与描述类的TypeScript接口间接相关
虽然typeof
a类是function
,但Typescript中没有class(也没有Interface),它是所有类中的超类。
然而,所有功能都由Function
接口
我想为了与Javascript一致,Function
可能应该是一个类,所有函数都应该是该类的,所有类都应该扩展它…
- ES6构造函数返回基类的实例
- 如何在下面的ES6循环中获得前面的文本
- es6 相当于下划线查找位置
- 如何在Javascript/ES6中的Aurelia浏览器应用程序中使用AWS S3
- ES6生成器:.next()的输入值
- 简单的ES6承诺问题-交换解决和拒绝参数
- 映射数组ES6时考虑空值
- @@(“at at”)在ES6 JavaScript中是什么意思
- 在ES6中,模块将导致多个网络调用,因为两个模块不能在单个文件中定义
- ES6是否引入了一种机制来生成块范围的函数语句(而不是表达式)
- 使用Ember-cli项目中的ES6库
- JShint-.jshintrc中的ES6有esversion,但仍收到警告(使用atom)
- 在ES6 Promise中,我应该在解决/拒绝之前使用return吗
- Unexpected Transpile ES6>ES5
- 可以合并或嵌套ES6导入
- ES6 模板文字是否比 eval 更安全
- Es6:能够在设置/更新/删除对象属性时调用自定义方法
- 数组值的排序以匹配另一个数组ES6
- 使用 webpack 和 ts-loader 重新导出 ES6 模块
- 从非类继承ES6/TS类