小心TypeScript编写出无用的代码
本文翻译自The Daily WTF网站,原作者的大意是TypeScript相比JavaScript确实有很多优势,但是这个优势其实是编译器带来的。在编写TS代码的时候应当重视编译器的警告,因为有些代码转换成JS后可能并不会有错误,但是实际上因为JS缺少TS的一些特性,可能会导致代码运行出问题。
TypeScript相比JavaScript有一定的优势。编译时检查能够发现很多代码编写的错误,相比浏览器而言发展更快,与最新的行业标准更匹配(编译器将那些与浏览器解释转化讨厌的细节全部搞定了),而且TS还有更方便好用的语法糖。
如果你在使用TS,你可以使用编译器发现代码中很多丑陋的问题,而你要做的就是把那些标红的错误修改掉。或者,你可以像昆塔斯(Quintus)的同事那样,就像,呃,下面的代码。
/**
* Container holding definition information.
*
* @param String version
* @param String date
*/
export class Definition {
private id: string;
private name: string;
constructor(private version, private data) {}
/**
* get the definition version
*
* @return String version
*/
getVersion() {
return this.id;
}
/**
* get the definition date
*
* @return String date
*/
getDate() {
return this.name;
}
}
现在,如果你想在TS的环境中运行这段代码,你会发现在编译和转换成JS过程中,编译器合理地指出了一堆警示。然而,如果你用命令行工具tsc编译,默认设置下却没有任何警示。
因此,如果TypeScript代码写得不好,编译器工具可以告诉你代码的问题所在,但是前提是你得要求编译器工具按你的要求检查。
不管怎么说,很容易理解这个糟糕的代码会发生什么:这显然是通过复制/粘贴完成的编码。代码定义了一个拥有id和name属性的类。复制/粘贴的时候却弄成了version和date,然后可能是代码还没改完,中途被什么事打断了。他们也像模像样地检查了代码,但是却没人发现——直到昆塔斯站在旁边的时候。
再来聊聊这段代码,你会发现id和name是私有属性,构造器又定义了另外两个属性(通过构造器进行关联):私有的version和data参数。因此,当调用构造函数的时候,实际上初始化了根本没有对应访问方法的两个私有成员。确实是有成员访问方法,但是实际使用却是id和name的,而id和name既没有在构造方法初始化,也没有外部设置的入口。
由于TS编译成了JS,而JS是没有private这个关键字的,因此编译成JS并不会有任何警告。我猜想这段代码最终胎死腹中,并没有真正派上用场。如果需要用的话,我敢打赌是下面这样的。
let f = new Definition();
f.id = "1.0.1"
f.name = "28-OCT-2020"
…
let ver = f.getVersion();
这段代码可以按照原先的开发者预期那样正常运行,但是如果他们这么做的话,TS编译器就会报警——然而,如你所见,他们并不真的关心编译器的那些警告。