这个 TypeScript 技巧会让你大吃一惊

2024年 5月 10日 57.7k 0

从字符串数组中提取自定义类型

在 TypeScript 的世界里,自定义类型从字符串数组中显现,就像隐藏的宝石。

TypeScript 是一个操纵现有数据和发展良好实践的神奇工具。

今天,我们将探索如何以正确的方式从字符串数组中提取全名,以确保产生干净的类型安全输出。

那么,不多说了……让我们直接开始吧。

这个 TypeScript 技巧会让你大吃一惊-1

问题

首先让我们通过检查这段代码来理解其中的问题:

const names = ["Daniel Craciun", "John Doe", "Harry Pigeon"]

const findName = (surname: string) => {
  return names.find((name) => name.includes(surname))
}

// 我们可以传入任何字符串,这是不理想的。

console.log(findName("Craciun")) // 输出:Daniel Craciun
console.log(findName("Doee")) // 输出:undefined

这段代码使用一个名字数组来进行搜索。

函数 findName 接受一个字符串 surname 并返回关联的全名。

问题出现在当你在 findName 函数中输入 "Doee" 时。

这个不显眼的拼写错误导致输出了 undefined,这可能会导致后续的错误,因为没有任何东西阻止我们犯这种错误。

这就是 TypeScript 发挥作用的地方。

如果我们确保 findName 只接受字面上的姓氏,即 Craciun、Doe、Pigeon,那么当我们输入像 "Doee" 这样在名字数组中不存在的输入时,编译器应该会提出警告。

解决方案

我们已经确定了 findName 的有效参数只能是所有现有的姓氏。

为了实现这一点,我们创建了一个名为 ExtractSurname 的泛型类型。

ExtractSurname 的代码可能看起来有点复杂,但我们将一步步拆解它:

type ExtractSurname = T extends `${infer Firstname} ${infer Surname}` ? Surname : null

这里 ExtractSurname 接受一个泛型参数 T,它引用任何字面字符串,使用 extends 操作符。在 ExtractSurname 中,T 的值将等于 "Daniel"。

接下来我们应用 TypeScript 三元运算符,它类似于 JavaScript 三元运算符,但我们是在比较类型而不是实际数据。

我们知道我们的名字数组的格式是“ ”,所以这里使用 infer 关键字从 T 中提取子类型。

在 ExtractSurname 中:

  • infer Firstname = “Daniel”
  • infer Surname = “Craciun”

最后,如果输入满足我们的“ ”格式,返回 Surname 作为类型,否则返回 null。

好的,我们的 ExtractSurname 类型准备好了。

现在我们需要一个 Surname 类型来表示 names 中所有的姓氏。

type ExtractSurname = T extends `${infer Firstname} ${infer Surname}` ? Surname : null

const names = ["Daniel Craciun", "John Doe", "Harry Pigeon"] as const
type Surname = ExtractSurname

names 满足 ExtractSurname 的格式 “* *”。

我们使用 as const 将 names 的类型从字符串缩小到字面字符串数组。

这意味着我们转换 names 的类型从 string 到:readonly [“Daniel Craciun”, “John Doe”, “Harry Pigeon”]。

参数 (typeof names)[number] 代表 names 中每个索引元素的类型:“Daniel Craciun” | “John Doe” | “Harry Pigeon”

最终,这是 Surname 的结果类型:type Surname = “Craciun” | “Doe” | “Pigeon”

最后一步是用下面的新函数 findNameUsingSurname 更新我们之前定义的 findName 函数:

// 接收一个实际的 `Surname` 而不是一般的字符串。
const findNameUsingSurname = (surname: Surname) => {
  // 注意:我们需要后缀运算符 "!" 来断言 "find" 函数不返回未定义的值。
  return names.find((name) => name.includes(surname))!
}

// 唯一可接受的输入:"Craciun", "Doe", "Pigeon" = 最大类型安全
const fullName = findNameUsingSurname("Craciun")

// 输出:"Daniel Craciun"
console.log(fullName)

而这里是 TypeScript 编译器如我们所期待的那样施展它的魔法:

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论