首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

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

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

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

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

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

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

问题

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

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 string> = T extends `${infer Firstname} ${infer Surname}` ? Surname : null

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

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

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

在 ExtractSurname<“Daniel Craciun”> 中:

• infer Firstname = “Daniel”

• infer Surname = “Craciun”

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

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

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

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

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

type Surname = ExtractSurname<(typeof names)[number]>

1. names 满足 ExtractSurname 的格式 “*<名> <姓>*”。

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

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

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

2. 最终,这是 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 编译器如我们所期待的那样施展它的魔法:

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OgAk5DjMHbbv8WXFC8Y73vUQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券