我承认,我不太懂 TypeScript
前几天,我在处理乐观更新的代码中遇到了一个错误,于是我向同事Filip寻求帮助。Filip 是一位 TypeScript 专家,他提到这个satisfies
关键字是我正在寻找的解决方案的一部分。
Satisfies
?这到底是什么?为什么我以前从未听说过?我的意思是,我已经使用 TypeScript 有一段时间了,所以我很惊讶我自己都不知道它。
此后不久,我偶然发现了 @yacineMTB 发的这条推文,他是X.com (又名 Twitter) 的多产喋喋不休者和工程师:
例如,为什么我不能直接运行TypeScript文件?如果我需要初始化整个目录和项目,那么脚本语言有什么意义呢?
我再次开始疑惑,为什么我之前不知道 TypeScript 的知识。为什么不能运行 TypeScript 文件?脚本语言和编译语言有什么区别?
我突然意识到,我并不十分了解我几乎每天都在用的语言的一些基本知识,而我正是用它来创建诸如Open SaaS(一个免费的开源 SaaS 启动器)之类的东西的。
所以我决定退一步,对这些主题进行一些调查。在本文中,我将与大家分享我学到的一些最重要的东西。
TypeScript 是什么类型的脚本?
您可能已经听说过 TypeScript 是 JavaScript 的“超集”。这意味着它是 JavaScript 之上的一个附加层,在这种情况下,它允许您向 JavaScript 添加静态类型。
有点像 TypeScript 是 JavaScript 的高级版本。或者换句话说,如果 JavaScript 是特斯拉 Model 3 的基础型号,那么 TypeScript 就是 Model X Plaid。呜呜呜。
但由于它是JavaScript 的超集,因此它的运行方式实际上与 JavaScript 本身不同。例如,JavaScript 是一种脚本语言,这意味着代码在执行过程中会被逐行解释。它被设计成可以在不同操作系统和硬件配置的 Web 浏览器中运行。这与 C 等低级语言不同,后者需要先编译为特定系统的机器码,然后才能执行。
因此,JavaScript 不必先进行编译,而是由 JavaScript 引擎进行解释。另一方面,TypeScript 必须先转换(或“转编译”)为 JavaScript,然后才能由浏览器中的 JavaScript 引擎执行(或作为独立的 NodeJS 应用程序执行)。
这个过程看起来有点像这样:
→ Write TypeScript Code
→ “Transcompile” to JavaScript
→ Interpret JavaScript & Check for Errors
→ JavaScript Engine Compiles and Executes the Code
很有趣,对吧?
但是现在我们已经解决了一些理论问题,让我们继续讨论一些更实际的东西,比如 TypeScript 所熟知的东西:类型!
顺便一提…
我们正在Wasp努力创建最好的开源 React/NodeJS 框架,让您快速行动!
这就是为什么我们有现成的全栈应用模板,例如带有 TypeScript 的 ToDo 应用。您所要做的就是安装 Wasp:
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
并运行:
wasp new -t todo-ts
您将获得一个开箱即用的带有 Auth 和端到端 TypeSafety 的全栈 ToDo 应用程序,以帮助您学习 TypeScript,或者只是快速安全地开始构建一些东西 :)
玩转satisfies
还记得我向同事寻求帮助时,他的解决方案涉及satisfies
关键字吗?为了更好地理解它,我决定打开编辑器并尝试一些基本示例,我发现这是我学到的最有用的东西。
首先,我们以 person 对象为例,将其类型化为Record
可以接受一组PossibleKeys
和string
或number
作为值的 。看起来就像这样:
type PossibleKeys = "id" | "name" | "email" | "age";
const person: Record<PossibleKeys, string | number> = { }
我们输入常量的方式person
称为类型注释。它直接位于变量名之后。
让我们开始向该person
对象添加键和值:
type PossibleKeys = "id" | "name" | "email" | "age";
const person: Record<PossibleKeys, string | number> = {
id: 12,
name: "Vinny",
email: "vince@wasp-lang.dev",
age: 37,
}
看起来很简单,对吧?
现在,让我们看看 TypeScript 如何推断属性的类型person
:
有趣的是,当我们将鼠标悬停在 上时email
,我们会看到 TypeScript 告诉我们 email 是 astring
或 a的联合类型number
,尽管我们明确地将其定义为 a string
。
如果我们尝试在此类型上使用某些string
方法,这将产生一些意想不到的后果。让我们尝试该split
方法,例如:
我们收到一个错误,提示该方法对类型 不起作用number
。这是正确的。但这很烦人,因为我们知道这email
是一个字符串。
让我们satisfies
通过将类型移到常量定义的末尾来解决这个问题:
type PossibleKeys = "id" | "name" | "email" | "age";
const person = {
id: 12,
name: "Vinny",
email: "vince@wasp-lang.dev",
age: 37,
} satisfies Record<PossibleKeys, string | number>;
现在,当将鼠标悬停在email
属性上时,我们将看到它被正确推断为string
:
太棒了!现在,我们可以毫无问题地split
将其转换email
为字符串数组。
这就是satisfies
真正的亮点。它让我们验证表达式的类型是否与某个类型匹配,同时推断出最窄的类型。
超额财产检查
但是我在使用过程中注意到的另一个奇怪的事情satisfies
是,如果我直接在变量上使用它而不是在中间变量上使用它,它的行为会有所不同,如下所示:
// Directly on object literal
const person = { } satisfies PersonType;
// Using on intermediate variable
const personIntermediate = person satisfies PersonType
具体来说,如果我向对象添加另一个person
类型中不存在的属性,比如isAdmin
,直接使用时会出错,但使用中间变量则不会出错:
直接使用
satisfies
使用
satisfies
中间变量
您可以看到,在示例 2 中,没有错误并且 person“满足” PersonType
,尽管在示例 1 中并非如此。
这是为什么?
嗯,这实际上与 JavaScript 的基本工作原理有关,与satisfies
关键字关系不大。让我们来看看。
上述例子中发生的过程就是所谓的“多余财产检查”。
过多的属性检查实际上是规则的例外。TypeScript 使用所谓的“结构类型系统”。这只是一种奇特的说法,即如果一个值具有所有预期的属性,它将被使用。
因此,使用personIntermediate
上述示例,TypeScript 不会抱怨具有中不存在的person
额外属性。它具有所有其他必要的属性,例如、、和,因此 TypeScript 以这种中间形式接受它。isAdminPersonTypeidnameemailage
但是,当我们直接在变量上声明类型时,就像我们在示例 1 中所做的那样,我们会收到 TypeScript 错误:“'isAdmin' 在类型 'PersonType' 中不存在”。这是过度属性检查在起作用,它可以帮助你避免犯愚蠢的错误。
记住这一点是很好的,因为这将帮助你避免意想不到的副作用。
例如,假设我们将人员类型更改为具有可选isAdmin
属性,如下所示:
type PersonType = {
id: number,
name: string,
isAdmin?: boolean, // 👈 Optional
}
person
如果我们意外地用isadmin
属性isAdmin
而不是直接声明类型来定义,会发生什么?
我们不会从 TypeScript 中得到任何错误,因为person
实际上确实满足了所有必要的类型。isAdmin
类型是可选的,并且它不存在person
,但这并不重要。而且您已经做了一个简单的 type-o,现在尝试访问isAdmin
属性,但它不起作用:
哎呀!让我们用类型注释来修复它,我们立即声明类型:
很好。因为我们在第 58 行使用了直接类型注释,所以我们可以获得 TypeScript 的多余属性检查的好处。
谢谢,TypeScript!🙏
如果您发现此内容有用,并且希望看到更多类似的内容,您可以在 GitHub 上给 Wasp 一颗星,从而轻松地帮助我们! 。
待续…
感谢您加入我的旅程第一部分,以更好地了解我们每天使用的工具。
这将是一系列持续的文章,我将继续以更具探索性、更不具结构性的方式分享我所学的知识。希望您觉得其中的某些部分有用或有趣。
让我知道你接下来想看什么!你喜欢这种风格吗?你会改变一些东西吗?添加或删除一些东西?或者你对最近学到的东西有什么看法或类似的故事吗?
如果是的话,请在评论中告诉我们,下次再见:)