我最初写的这个答案当 Typescript 仍然热卖时。五年后,这是一个很好的概述,但请看下面的Lodewijk 答案更深入
TypeScript是 JavaScript 的超集,主要提供可选的静态类型,类和接口。其中一个重要好处是使 IDE 能够在您键入代码时提供更丰富的环境来发现常见错误。
要了解我的意思,请观看Microsoft关于该语言的介绍性视频 。
对于大型 JavaScript 项目,采用 TypeScript 可能会产生更强大的软件,同时仍可部署常规 JavaScript 应用程序。
它是开源的,但如果您使用支持的 IDE,则只能在键入时获得聪明的 Intellisense。最初,这只是微软的 Visual Studio(也在Miguel de Icaza 的博客文章中提到)。目前, 其他 IDE 也提供 TypeScript 支持 。
有CoffeeScript ,但这确实有不同的用途。恕我直言,CoffeeScript 为人类提供了可读性,但 TypeScript 还通过其可选的静态类型为工具提供了深度可读性(请参阅最近的博客文章以获得更多批评)。还有Dart,但这完全取代了 JavaScript(尽管它可以生成 JavaScript 代码 )
举个例子,这里有一些 TypeScript(您可以在TypeScript Playground 中使用它 )
class Greeter {
greeting: string;
constructor (message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
这是它将产生的 JavaScript
var Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
})();
注意 TypeScript 如何定义成员变量和类方法参数的类型。这在转换为 JavaScript 时被删除,但 IDE 和编译器使用它来发现错误,例如将数字类型传递给构造函数。
它还能够推断未明确声明的类型,例如,它将确定greet()
方法返回一个字符串。
许多浏览器和 IDE 通过源映射提供直接调试支持。有关更多详细信息,请参阅此 Stack Overflow 问题: 使用 Visual Studio 调试 TypeScript 代码
虽然接受的答案很好,但我觉得此刻真的没有做 TypeScript 公正。现在不再是早期了。现在,TypeScript 正在寻找更多的应用,其中一些流行的框架是用 TypeScript 编写的。您现在应该选择 TypeScript 而不是 JavaScript 的原因很多。
TypeScript 是现代 JavaScript + 类型。它是关于尽早捕获错误并使您成为更高效的开发人员,同时利用 JavaScript 社区。
JavaScript 通过 ECMAScript 标准进行标准化。较旧的浏览器不支持较新的 ECMAScript 标准的所有功能(请参阅此表 )。 TypeScript 支持新的 ECMAScript 标准,并将它们编译为您选择的(较旧的)ECMAScript 目标(当前目标是 3,5 和 6 [又名 2015])。这意味着您可以使用 ES2015 及更高版本的功能,如模块,lambda 函数,类,扩展运算符和解构,同时保持向后兼容旧版浏览器。
类型支持不是 ECMAScript 标准的一部分,可能永远不会归因于解释性质而不是 JavaScript 的编译性质。 TypeScript 的类型系统非常丰富,包括:接口,枚举,混合类型,泛型,联合 / 交集类型,访问修饰符等等。 TypeScript 的官方网站概述了这些功能。今天的打字稿类型系统与其他类型语言相当,在某些情况下可以说更强大。
与其他编译为 JavaScript 的语言相比,TypeScript 具有独特的理念。 JavaScript 代码是有效的 TypeScript 代码; TypeScript 是 JavaScript 的超集。您几乎可以将.js
文件重命名为.ts
文件并开始使用 TypeScript(请参阅下面的 “JavaScript 互操作性”)。 TypeScript 文件被编译为可读的 JavaScript,因此可以进行迁移,并且理解编译的 TypeScript 并不困难。 TypeScript 建立在 JavaScript 成功的基础上,同时改善了它的弱点。
一方面,您有未来的证明工具,它们采用现代 ECMAScript 标准并将其编译为较旧的 JavaScript 版本,其中 Babel 是最受欢迎的版本。另一方面,您的语言可能与针对 JavaScript 的 JavaScript 完全不同,如 Coffeescript,Clojure,Dart,Elm,Haxe,ScalaJs 以及更多主机(请参阅此列表 )。这些语言虽然可能比 JavaScript 未来可能带来的更好,但却没有找到足够的采用它们的未来得到保证的风险。您可能也很难找到有经验的开发人员使用这些语言,尽管您会发现这些语言通常会更热情。使用 JavaScript 的 Interop 也可以更多地涉及,因为它们实际上远离了 JavaScript。
TypeScript 位于这两个极端之间,从而平衡风险。 TypeScript 不是任何标准的危险选择。如果您熟悉 JavaScript,则需要花费很少的精力,因为它不是一种完全不同的语言,具有出色的 JavaScript 互操作性支持,并且最近已经有很多采用。
JavaScript 是动态类型的。这意味着 JavaScript 在运行时实际实例化之前不知道变量的类型。这也意味着可能为时已晚。 TypeScript 为 JavaScript 添加了类型支持。如果你正确地玩牌,你可以完全根除某些类型的某些变量的错误假设所导致的错误(你输入代码的严格程度如何,或者你输入的代码完全取决于你)。
通过使用类型推断,TypeScript 使输入更容易,而且更不那么明确。例如:TypeScript 中的var x = "hello"
与var x : string = "hello"
。该类型只是从其使用中推断出来的。即使你没有明确地键入类型,它们仍然可以防止你做一些否则会导致运行时错误的事情。
默认情况下,可选择键入 TypeScript。例如, function divideByTwo(x) { return x / 2 }
是 TypeScript 中的有效函数,可以使用任何类型的参数调用,即使使用字符串调用它也会显然导致运行时错误。就像你习惯于 JavaScript 一样。这是有效的,因为当没有显式指定类型并且无法推断类型时,例如在 divideByTwo 示例中,TypeScript 将隐式指定类型any
。这意味着 divideByTwo 函数的类型签名自动变为function divideByTwo(x : any) : any
。有一个编译器标志可以禁止此行为: - --noImplicitAny
。启用此标志可以提供更高程度的安全性,但也意味着您需要进行更多的打字。
类型具有与之相关的成本。首先,有一个学习曲线,其次,当然,使用适当的严格类型设置代码库会花费你更多的时间。根据我的经验,这些成本对于您与他人共享的任何严格代码库都是完全值得的。 Github 对编程语言和代码质量的大规模研究表明, “静态类型语言通常比动态类型更不易缺陷,并且强类型在同一方面优于弱类型”。
有趣的是,这篇论文发现 TypeScript 比 JavaScript 更不容易出错:
对于那些具有正系数的人,我们可以预期该语言与其他条件相同,更多的缺陷修复。这些语言包括 C,C ++, JavaScript ,Objective-C,Php 和 Python。语言 Clojure,Haskell,Ruby,Scala 和TypeScript都具有负系数,这意味着这些语言不太可能导致缺陷修复提交。
TypeScript 的开发经验是对 JavaScript 的重大改进。 TypeScript 编译器会在其丰富的类型信息上实时通知 IDE。这提供了几个主要优点。例如,使用 TypeScript,您可以安全地在整个代码库中进行重构等重构。通过代码完成,您可以获得库可能提供的任何功能的内联帮助。不再需要记住它们或在在线参考中查找它们。在您忙于编码时,编译错误会直接在 IDE 中报告,并带有红色波浪线。总而言之,与使用 JavaScript 相比,这可以显着提高工作效率。人们可以花更多的时间编写代码,减少调试时间。
有各种各样的 IDE 对 TypeScript 有很好的支持,比如 Visual Studio Code,WebStorm,Atom 和 Sublime。
表单的运行时错误cannot read property 'x' of undefined
或undefined is not a function
,这通常是由 JavaScript 代码中的错误引起的。开箱即用 TypeScript 已经降低了发生这类错误的可能性,因为不能使用 TypeScript 编译器不知道的变量(除了any
类型变量的属性)。尽管错误地利用设置为undefined
的变量仍然是可能的。但是,使用 2.0 版本的 TypeScript,您可以通过使用非可空类型来消除这些类型的错误。其工作原理如下:
启用严格空检查( --strictNullChecks
编译器标志)后,TypeScript 编译器将不允许将undefined
分配给变量,除非您明确声明它是可空类型。例如, let x : number = undefined
将导致编译错误。这完全符合类型理论,因为undefined
不是数字。可以将x
定义为number
的和类型,并将其undefined
为undefined
: let x : number | undefined = undefined
。
一旦知道类型可以为空,意味着它的类型也可以是null
或undefined
,TypeScript 编译器可以通过基于控制流的类型分析来确定您的代码是否可以安全地使用变量。换句话说,当您通过例如if
语句检查变量undefined
,TypeScript 编译器将推断代码控制流的该分支中的类型不再可为空,因此可以安全地使用。这是一个简单的例子:
let x: number | undefined;
if (x !== undefined) x += 1; // this line will compile, because x is checked.
x += 1; // this line will fail compilation, because x might be undefined.
在 2016 年的会议期间,TypeScript Anders Hejlsberg 的联合设计师对此功能进行了详细解释和演示: 视频 (从 44:30 到 56:30)。
要使用 TypeScript,您需要一个构建过程来编译为 JavaScript 代码。构建过程通常只需几秒钟,具体取决于项目的大小。 TypeScript 编译器支持增量编译( --watch
编译器标志),因此可以更快地编译所有后续更改。
TypeScript 编译器可以在生成的. js 文件中内联源地图信息或创建单独的. map 文件。调试实用程序(如 Chrome DevTools 和其他 IDE)可以使用源映射信息将 JavaScript 中的行与在 TypeScript 中生成它们的行相关联。这使您可以直接在 TypeScript 代码上设置断点并在运行时检查变量。源地图信息非常好用,它早在 TypeScript 之前就已存在,但调试 TypeScript 通常不如直接使用 JavaScript 时那么好。以this
关键字为例。由于自 ES2015 以来this
关键字围绕闭包的语义已经改变, this
在运行时可能实际上存在一个名为_this
的变量(参见本答案 )。这可能会在调试期间使您感到困惑,但如果您了解它或检查 JavaScript 代码,通常不会出现问题。应该指出的是,巴贝尔遇到了完全相同的问题。
TypeScript 编译器可以执行一些其他技巧,例如基于装饰器生成拦截代码,为不同的模块系统生成模块加载代码以及解析JSX 。但是,除了 Typescript 编译器之外,您可能还需要一个构建工具。例如,如果要压缩代码,则必须在构建过程中添加其他工具才能执行此操作。
有适用于Webpack , Gulp , Grunt和几乎任何其他 JavaScript 构建工具的 TypeScript 编译插件。 TypeScript 文档有一个关于与构建工具集成的部分。如果您想要更多的构建时间检查,也可以使用linter 。还有大量的种子项目可以让你开始使用 TypeScript 以及一些其他技术,如 Angular 2,React,Ember,SystemJs,WebPack,Gulp 等。
由于 TypeScript 与 JavaScript 密切相关,因此具有出色的互操作性功能,但在 TypeScript 中使用 JavaScript 库需要一些额外的工作。需要TypeScript 定义 ,以便 TypeScript 编译器能够理解像_.groupBy
或angular.copy
或$.fadeOut
这样的函数调用实际上不是非法语句。这些函数的定义放在.d.ts
文件中。
定义可以采用的最简单的形式是允许以任何方式使用标识符。例如,当使用Lodash 时 ,单行定义文件declare var _ : any
将允许你在_
上调用你想要的任何函数,但当然你仍然可以犯错: _.foobar()
将是一个合法的 TypeScript 调用,但在运行时当然是非法调用。如果您需要正确的类型支持和代码完成,您的定义文件需要更加精确(请参阅示例的 lodash 定义 )。
TypeScript 编译器会自动理解预先打包自己的类型定义的Npm 模块 (请参阅文档 )。对于几乎没有任何其他半流行的 JavaScript 库,它不包含自己的定义,那里的某些人已经通过另一个 npm 模块提供了类型定义。这些模块以 “@ types /” 为前缀,来自名为DefinitelyTyped的 Github 存储库。
有一点需要注意:类型定义必须与您在运行时使用的库的版本相匹配。如果他们不这样做,TypeScript 可能会禁止您调用函数或取消引用存在的变量或允许您调用函数或取消引用不存在的变量,这只是因为类型与编译时的运行时不匹配。因此,请确保为正在使用的库的正确版本加载正确版本的类型定义。
说实话,这有点麻烦,这可能是你不选择 TypeScript 的原因之一,而是选择像 Babel 这样的东西,它们根本不需要获得类型定义。另一方面,如果您知道自己在做什么,则可以轻松克服由于错误或缺少定义文件而导致的任何问题。
任何.js
文件都可以重命名为.ts
并通过 TypeScript 编译器运行,以在语法上获得与输出相同的 JavaScript 代码(如果它首先在语法上是正确的)。即使 TypeScript 编译器出现编译错误,它仍然会生成.js
文件。它甚至可以使用--allowJs
标志接受.js
文件作为输入。这允许您立即从 TypeScript 开始。不幸的是,编译错误很可能在开始时发生。人们需要记住,这些不是像你可能习惯与其他编译器一样的显示停止错误。
在将 JavaScript 项目转换为 TypeScript 项目时,编译错误在 TypeScript 的本质中是不可避免的。 TypeScript 检查所有代码的有效性,因此需要了解所有使用的函数和变量。因此,需要为所有类型定义类型定义,否则必然会发生编译错误。如上一章所述,几乎任何 JavaScript 框架都有.d.ts
文件,可以通过安装DefinitelyTyped 软件包轻松获取。但是,您可能已经使用了一些不具有 TypeScript 定义的模糊库,或者您已经填充了一些 JavaScript 原语。在这种情况下,您必须为这些位提供类型定义,以便编译错误消失。只需创建一个.d.ts
文件并将其包含在 tsconfig.json 的files
数组中,以便 TypeScript 编译器始终考虑它。在其中声明 TypeScript 不知道的那些位是any
类型。一旦消除了所有错误,您可以根据需要逐步为这些部分引入打字。
为了将 TypeScript 引入构建管道,还需要一些(重新)配置构建管道的工作。正如编译章节中所提到的,那里有很多很好的资源,我鼓励你寻找使用你想要使用的工具组合的种子项目。
最大的障碍是学习曲线。我鼓励你先做一个小项目。看看它是如何工作的,如何构建,它使用哪些文件,如何配置,如何在 IDE 中运行,如何构建,使用哪些工具等等。当你知道将大型 JavaScript 代码库转换为 TypeScript 时是可行的你在做什么。阅读此博客,例如在 72 小时内将 600k 行转换为打字稿 。只需确保在跳跃之前掌握好语言。
TypeScript 是开源的(Apache 2 许可,请参阅github )并由 Microsoft 提供支持。 C#的首席架构师Anders Hejlsberg正在带领该项目。这是一个非常活跃的项目; TypeScript 团队在过去几年中已经发布了许多新功能,并且还计划推出许多很棒的功能(参见路线图 )。
在2017 年 StackOverflow 开发人员调查中, TypeScript 是最受欢迎的 JavaScript 转换器(总体排名第 9 位),并且在最受欢迎的编程语言类别中获得第三名。
TypeScript 的功能类似于 CSS 或更少的功能。它们是它的超级集合,这意味着您编写的每个 JS 代码都是有效的 TypeScript 代码。另外,您可以使用它添加到语言中的其他好东西,并且转换后的代码将是有效的 js。您甚至可以设置您希望生成代码的 JS 版本。
目前 TypeScript 是 ES2015 的超级集合,因此可能是开始学习新 js 功能并转换为项目所需标准的不错选择。