我是如何学会停止担心并爱上 TypeScript 的

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

当我开始写这篇文章时,最初的工作标题是“Angular 没有毁掉 JavaScript,TypeScript 做到了”。然而,这并不是本文的真正结论(毕竟是“链接诱饵”)。我太爱你们了,不能对你们那样做。

但是让我先说一些有点煽动性的话来解释为什么我最初认为 TypeScript 实际上确实毁了 JavaScript。

JavaScript,欢迎来到地狱

我记得当 TypeScript 首次发布时,我观看了 Anders Hejlsberg 对类型、类、接口和其他此类静态语言进行一些演示的视频 ,所有这些都被转换为纯 JavaScript。

我想,“不。不不不不不不不!

我讨厌类型

我一直很喜欢 JavaScript。从我第一眼看到它的那一刻起。我学习的第一门语言是 ActionScript,然后是 C#。我讨厌 C#。好吧,不是 C# 的语法,但我绝对希望看到编译器在地狱中腐烂。

在我看来,编译器是不必要的愚蠢。

考虑以下…


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

真的吗?语言中只有一个空值,你无法处理?

“好吧,没有伯克。因为你必须知道将什么类型设置为 null,以防它以后不为 null。”

我不确定为什么当我们实际上对一种类型感兴趣时我们不能只处理它,但是好吧,我承认这一点。


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

如果不先说明它可以为空,就不能将整数的值设置为空。在这一点上,我已经需要太多的体力劳动了。我在这里尝试解决实际问题,我没有时间通过​​数字等基本概念来手持计算机。我确信可空类型有充分的学术理由,但 我不在乎

作为一名 C# 开发人员,我花了很多时间来对抗类型。就这样。这甚至还没有考虑诸如反射之类的事情,这是 JavaScript 中的一件微不足道的事情,等同于 C# 中的绝对马戏团。

我对类型化系统的根本问题是,它们迫使您提前知道您将要使用的每个传入数据结构的确切格式,这通常是不可能的。

考虑这样一种情况,您有一个数据库在整数字段中接受空类型。假设您正在使用一个您无法更改的模块,该模块使用不可为空的整数类型。你如何处理空场景?通常,它是这样的:


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

在这一点上,除了感觉生活完全失败之外,您还承担了技术债务,因为您被迫破坏代码以取悦编译器。

那么,如果强类型如此糟糕,为什么还有那么多开发人员喜欢它呢?一句话—​​—提高生产力。

提高生产力

好吧,就两个字。

我知道我刚刚提出了类型如何导致开发人员承担技术债务的案例,然后我说类型化语言提高了生产力,这不仅仅是一点点矛盾,但它是那些双刃剑之一。

类型使 IDE 不仅仅是一个文本编辑器。通常情况下,我们正在使用我们没有编写的代码,最常见的是通过使用框架。该框架有一个我们(至少在最初)一无所知的 API。这意味着为了使用框架提高工作效率,您不仅要知道如何编码,还要掌握其他人的代码。似乎有点不公平,不是吗?

学习整个框架不是一件容易的事。 5 年后,我仍然发现自己在 jQuery 文档中,那甚至不是一个复杂或庞大的 API 表面。当您谈论像 .NET 框架或 Cocoa 这样大的东西时,如果没有多年的经验,根本无法了解这一切。

让我们只使用 C# 中的字符串。根据 .NET 4.5 的官方文档 ,String 类有 8 个构造函数和 120 个方法 !这甚至不包括扩展方法或运算符。

相比之下,JavaScript String 对象有 1 个构造函数和 21 个方法。即使只有 21 种方法,我仍然会不时地查找它们。

这就是 IDE 介入的地方,它使使用像 .NET 这样庞大的框架成为可能。我不需要参考字符串文档,我只需要开始使用字符串。当我这样做时,IDE 会让我知道我可以使用哪些方法,并会告诉我它们的作用,而我永远不会离开编辑器并失去上下文。

这就是我们最终使用 TypeScript 的地方。

到目前为止,强类型语言是一个孤注一掷的包袱。打字稿是不同的。它带来了打字的最佳部分——特别是通过 IDE 集成提高了生产力——并将可笑的铸造部分留为可选。直到最近我在做一些 NativeScript 开发时,我才完全领会到它的强大之处。

TypeScript 的价值

我最近一直在做很多 NativeScript 的工作,NativeScript 将 TypeScript 视为一等公民——真的是 BFF。事实上, NativeScript (tns_modules) 中的所有跨平台模块 都是用 TypeScript 编写的。 AppBuilder For Visual Studio 为 NativeScript 提供了一个 TypeScript 模板,所以我决定试一试,看看在 Visual Studio 中使用 TypeScript 到底是什么样子。

如果我正在使用 NativeScript 编写一个简单的应用程序,我会从页面(移动视图)的标记开始。当我这样做时,Visual Studio 提示我一些我可以使用的事件,例如 NativeScript 中经常使用的 loaded 事件。这样做是因为 NativeScript 为 Visual Studio 提供了用于标记的架构定义。但提供编辑器魔力的是 Visual Studio。我还可以创建一个 TextField,而不必记住其中的标记是什么。

考虑处理 loaded 事件的 NativeScript 页面中的以下代码。这可以用标准的 ES5 JavaScript 编写,就像这样……


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

这是 CommonJS,NativeScript 也支持它作为一等公民。这样做的问题是,我经常发现自己回到 NativeScript 文档 ,试图记住哪些属性在哪些对象上,哪些对象传递给哪些事件。

这是这段代码中发生的事情……

  • 加载的事件已定义。
  • 获取对当前页面 (xml) 的引用。
  • 获得对 TextField 的引用。
  • TextField 的文本更改为“Hello Indeed”

问题是这只是 JavaScript,我必须在这里了解很多关于 NativeScript 的知识。我必须知道加载事件,我必须知道该事件接收什么对象,我必须知道如何获取对页面的引用。我必须知道我需要视图模块才能获得该引用,我必须知道视图模块上的方法是什么以及它需要什么参数。最后,我需要知道在控件实例上调用什么方法来设置它的文本。

这需要跟上 很多 ,而我们并没有真正做太多。此外,这些代码在设计时都没有经过检查,因此任何空对象或方法引用都将导致应用程序意外崩溃。

现在让我们用 TypeScript 重写它,看看它是如何简化的。

由于 TypeScript 是 NativeScript 中的一等公民,我们可以在 .ts 文件中重写它

我只是假设我知道我需要一个基于我在 XML 中构建的 loaded 事件。


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

到目前为止,这看起来很熟悉。现在我们只需要知道 loaded 函数将接收什么类型的事件。 根据文档 ,所有事件都会收到一个可观察的。要使用可观察对象,我们需要导入一个。


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

如果你觉得这种导入很可怕,那么你就像我一样。这实际上是模块的 ES6 语法。使用 TypeScript 的好处之一是,它在可能的地方实现了相当多的 ES6,并在规范不完整或缺失的地方使用专有语法。

在这种情况下,我们说的是,“从 observable 文件中导入所有内容,并将其全部放入一个名为 observable 的对象中。”

现在,在我们走到这一步之后,看看魔术的展开。我知道我需要一个页面引用,所以我检查了 args 对象。

我有两个选择—— eventData object 。这是此次活动中仅有的两个属性。我可以放心地假设该对象是我所需要的。现在我需要将其转换为 page 。我需要 Page 模块来执行此操作,并且 TypeScript 知道该类型。


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

现在我可以将该对象转换为 Page 类型。

更多 ES6!我可以在 TypeScript 中使用 let const 关键字。我也可以只使用 var ,那也很好。现在我有了页面并且 TypeScript 知道它是一个 page 对象,我可以使用 getViewById 方法获取对 TextField 的引用。

这会返回一个通用 view 对象,它是所有 UI 组件的子类。这意味着我需要将其转换为 TextField 类型。

该 GIF 中发生了很多事情。请注意,我犯了一个错误并尝试通过方法设置文本。 TypeScript 帮我捕捉到了这一点,并指出这是一个属性,而不是方法。另请注意,我对 TextField 类型的 import 看起来比其他导入更有趣。这是因为 ES6 模块允许您将模块公开的任何属性或方法作为单个导入导入。这允许您从模块中挑选您想要的。

关于所有这一切,我最喜欢的一点是我知道,毫无疑问,当我运行它时,它会起作用。如果不是,那也不会,因为我犯了一些愚蠢的错误。浏览器是宽容的。本机应用程序不是。错误不只是炮击到控制台。它们会导致严重的应用程序崩溃,而对于移动应用程序,应用程序只会崩溃,根本不会告诉用户任何信息。我一直认为这是一种糟糕的用户体验。

最好的部分

现在最好的部分是什么?我仍然可以做到这一点...


 var i = 1;
var x = null; 

Console.WriteLine(i == x);

// ERROR: Cannot assign <null> to an implicitly-typed variable

哈哈!我赢了! IDE 的所有好处,没有强类型系统令人心碎的精神错乱。正如他们在销售游戏中所说,这是“双赢”。

你应该使用 TypeScript 吗?

这取决于。

我认为由于浏览器对你的蹩脚代码难以置信的宽容,你可以使用普通的 JavaScript。我不相信浏览器环境对您的代码有如此严格的要求。老实说,我认为没有它你会迭代得更快。

然而 ……JavaScript 正在越来越多的地方被编写,包括用于创建本机移动应用程序。 NativeScript 只是一个例子。在这些情况下,我想您会想要 TypeScript 的强大功能。

我还认为 TypeScript 鼓励你学习 ES6。这是势在必行的,因为 ES6 正以惊人的速度向我们袭来。这是一个相当大的变化,你不可能永远避免它。 TypeScript 是一种很好的方式来简化一些基本的 ES6 概念,例如模块。

无论如何,TypeScript 让我非常喜欢。我知道我更喜欢在我的 NativeScript 项目中使用它,并且我期待着用它做更多的事情,尤其是当 Angular 2 即将出现时。

TypeScript 支持现已在 AppBuilder Plugin For Visual Studio 中可用。