当代码使用可能会初始化的值时,编译器应发出错误。
// Error, 'Derived' declaration must be after 'Base'
class Derived extends Base { }
class Base { }
尽管抛出编译器错误是一个不错的解决方案,但也许编译器可以按正确的顺序输出类。 那将是一个杀手feature。 例如,编译器会跟踪依赖关系并根据该关系输出类,仅在无法解析依赖顺序时才会引发编译器错误。
编译器会跟踪依赖关系,并据此输出类,仅在无法解析依赖顺序时才会引发编译器错误。
我们应该提出一个新建议吗? 这就是为什么我目前使用AMD模块而不是TypeScript内部模块的原因。 RequireJS编译器使用我在整个代码库中指定的依赖项(使用require()
)来确定适当的模块序列化顺序。
链接到#274。 我们需要概述它的规则和范围
扩展案例似乎是进行词汇检查的不错选择; 我们只需要确保在词法上基类排在派生类之前。 还有其他情况我们应该考虑吗?
一个问题是,对类定义进行重新排序可能会静默地对静态初始化程序的顺序进行重新排序。 如果基类在派生类之后出现,我投票赞成在类定义站点上维护静态初始化程序代码,或者只是标记一个错误。
我认为从大型项目/维护的角度来看,多文件案例更加有趣和有用(毕竟这是打字稿的表面目标)。
因此,我认为我们需要考虑单文件输出模式下的输出顺序。 (能够获得此顺序以构建包含多个文件的html文件也很不错)。
以下是一些我认为可以确保排序的语句:
class X extends Y {} // ensure Y is defined in prior file
module { new X(); } // ensure X is defined in prior file
class S { static z = new Z(); } // ensure Z is defined in prior file
我们还可以将其扩展到使用前定义的函数和变量,而不仅仅是类。
PS我有一个原型。
我认为没有任何意图尝试为您重新排序发射,只是为了给错误提供一定的错误,以便确保在运行时失败的事情。
丹,我同意您在单个文件中进行重新排序,但是当使用--out合并多个文件时,编译器可以控制发出顺序,我希望它选择的顺序可以正常工作。
无论如何,在运行时, @ sparecycles函数都会提升到作用域的顶部。 因此功能并不有趣。 变量也被吊起,问题在于此时它们不会被初始化。 现在在初始化之前使用是一个不同的问题,我认为它在#274下得到了跟踪。
重新排序; 我们遵循的理念是让输出代码尽可能接近输入代码。 本质上,我们让用户代码通过,我们只是去除类型。 从这个意义上讲,错误将更符合我们到目前为止所做的工作。
至于实现,我最近在Let和Const中添加了词法顺序验证,可以将其提取出来作为常规检查,并用于这些不同的情况。 你可以在这里找到它:
https://github.com/Microsoft/TypeScript/blob/master/src/compiler/checker.ts#L329
我们需要清楚地确定我们正在检查的情况,因此绝对欢迎PR :)
是的,我同意我们不希望在单个打字稿文件中重新排序,但是在--out文件的情况下,用户未指定顺序,因此,我还是希望编译器尽最大努力选择有效的订单。
函数提升是一个很好的例子,它说明了我们不需要关心单个文件的情况,但是对于多个人来说,编译成多个文件并选择将它们包括在.html文件中的顺序是不容易的。 在使用变量时未定义变量是一个很好的例子,说明由于/// <reference>
行中的更改,编译器可能在其中引入意外行为。
但在--out文件的情况下,用户未指定顺序
事实并非如此。 我们这里有非常简单的规则-使用reference
标记所隐含的顺序以及命令行上文件的顺序。 在这两种情况下,用户都是向我们提供订单。 让编译器忽略用户提供的命令是危险的选择。 如果编译器决定的顺序与您喜欢的顺序不同怎么办? 您将如何覆盖? 如果一个顺序中断2个类而另一个顺序中断2个变量怎么办?
然后,我们不应该更改顺序,而至少应该(有选择权)警告用户编译器使用的顺序可能是错误的吗?
对。 我们不应该订购,而是错误。
TypeScript中相互递归类的正确习惯是什么? 实际定义之前的declare class
?
如果您的类在类型系统或实例方法中相互引用,那没问题。 唯一有问题的“相互递归”模式是这样的:
class Alpha {
static myFriendBeta = new Beta();
}
class Beta {
static myFriendAlpha = new Alpha();
}
您可以将其重写为clodule:
class Alpha {
}
class Beta {
static myFriendAlpha = new Alpha();
}
module Alpha {
export var myFriendBeta = new Beta();
}
好的,除了“基类应在派生类之前按词法定义”之外,我们希望作为该问题一部分实现的规则是什么?
禁止向前引用内部模块成员,例如
var x = M.fn(); // Should error
module M {
export function fn() {}
}
禁止转发引用枚举成员
var x = E.A; // Should error
enum E { A }
IMO从目前的角度来看,此问题的范围是相当有限的,因为人们通常不会在一个文件中定义所有代码:基类更可能存在于与派生类不同的文件中。
我建议@sparecycles中的以下内容也应作为此问题的解决方案的一部分:
然后,我们不应该更改顺序,而至少应该(有选择权)警告用户编译器使用的顺序可能是错误的吗?
“编译器使用的顺序”应包括tsconfig
指定的顺序。
当基类和派生类位于同一个文件中时,问题就不那么糟糕了:程序在启动时将崩溃,程序员将更改该文件中的顺序,问题将是_fixed_。
在使用多文件的情况下,编译器应以可疑的顺序连接多个文件时发出警告,因为该顺序可能会因微妙的原因而改变。
考虑在所有文件都位于单独的文件中的情况下,有一个基类和多个派生类的情况。 基类在其实现中使用一些派生类,因此它引用了它们,但仍需要将其放在输出中的第一位。 同样,所有派生类都需要引用基类。
好吧,相互引用的文件没有问题,如果A.ts相互引用B.ts,而X.ts包含A.ts,则输出顺序将为[B,A,X],并且如果它引用B.ts顺序为[A,B,X]。 (但是这些命令中只有一个可以在运行时运行。)这使事情变得脆弱,因为如果引用B或A,则编译将同样成功。
我的基础派生类系统问题的解决方案:为我的类层次结构添加index.ts,并在此文件中包含所有派生类,然后是基类。 这样可以确保输出将基类放在首位。 (完全违反直觉!)。 我发现,如果直接引用我想要的文件,最终将在派生文件之后生成基类。
编译器警告会非常好,但是如果能够将相互引用方案中的一个引用标记为发出顺序,而另一个仅用于引入声明,那将也很棒。 相互的发射顺序引用将是错误的。
(由于使用多文件输出(易于调试),因此我目前已实现了此功能,以自动生成Visual Studio / Typescript项目中的.js清单。很有兴趣,我会问我是否可以共享它。这基本上是Tarjan CC算法的两次运行。
发出警告时发出警告是错误的,使用明确的指令来稳定发出命令都将大大有助于使打字稿成为大型项目的可行语言……是吗?
我相当小的代码库(大约80个.ts文件)经常遇到此问题。 理想情况下,我不想在任何文件的顶部都没有任何<reference>
标签,并且编译器可以为我完成所有工作。
我的应用程序只有一个实例化类并执行应用程序的文件(我的合成根),一些添加扩展名的文件(例如,添加Array.prototype.distinct
),其余的只是类/接口定义。
在这种情况下,大多数代码都是公平的重新排序游戏,并且不应该手动要求<reference>
定义才能正确。 我认为类定义对于任何编译器重新排序都是公平的,应该将其定义到组合输出的顶部,而其余语句可以保留输入时的顺序。
编译器标志--looseSorting
可能吗? 似乎是一个颇受追捧的功能。
在ES6中的发出类声明中,我们在类声明之后进行静态属性分配。 这使得在计算属性名称中引用类静态属性成为use-before-definition。
发出的JS:
class C {
[C.p] () {} // Use before definition
[C.p+ C.e]() {} // Use before definition
[D.f] () {} // Use before definition
}
C.p = 10;
C.e = 20;
class D {
}
D.f = "hi";
我们只想在两种情况下警告此错误:计算属性名称引用其类的静态属性,或引用在其下方定义的其他类属性。
今天我们正在玩的简单示例,仅包括在任何测试中:
function f() {
function g() {
i = 10;
}
let i = 20;
g();
}
这将会是很好的得到的使用/定义排列g
左右i
。
不要忘记考虑在块范围内定义的函数。 按照JavaScript标准,这是未定义的行为,而且我知道至少Firefox和Chrome在其实现上存在分歧。
例如:
function f() {
if (true) {
g(); // iirc, g executes in Chrome, and is undefined in Firefox
function g() {
}
g(); // works in both browsers
}
}
只是说我们今天被这件事咬了,花了一些时间弄清楚发生了什么。
TypeScript v1.8.10,基于Webpack的构建,基类和派生类都在同一文件中定义,但(显然)顺序错误,没有编译错误或警告,即使源映射正常工作,错误调用堆栈也指向高度无用的位置(导入派生类的另一类的末尾)。
尽管没有进行全部讨论,但是作为急救措施,似乎编译器警告会有所帮助。 只是我们的2¢
我发现TS不支持开箱即用的功能是荒谬的。 造成的混乱类似于使用标准JS。 另外,虚拟方法,有人吗?
@MrGuardian OP中描述的复制已修复。 也许您可以在新问题或现有问题中进行说明,以更好地描述您遇到的问题?
(#12673)这是IMO应该出错的另外两种情况:
``
课堂测试
{
_b = this._a; //未定义,无错误/警告
_a = 3;
static _B = Test._A; // undefined, no error/warning
static _A = 3;
method()
{
let a = b; // Block-scoped variable 'b' used before its declaration
let b = 3;
}
}
``
@Spongman能否将其记录在另一期中? 谢谢!
最有用的评论
只是说我们今天被这件事咬了,花了一些时间弄清楚发生了什么。
TypeScript v1.8.10,基于Webpack的构建,基类和派生类都在同一文件中定义,但(显然)顺序错误,没有编译错误或警告,即使源映射正常工作,错误调用堆栈也指向高度无用的位置(导入派生类的另一类的末尾)。
尽管没有进行全部讨论,但是作为急救措施,似乎编译器警告会有所帮助。 只是我们的2¢