你绝对不能错过的TypeScript技巧
TypeScript,作为 JavaScript 的超集,不仅为开发者提供了类型安全的编程体验,还极大地促进了代码的可维护性、可读性以及健壮性。在数据处理的场景中,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 函数接受一个姓氏字符串并返回相关的全名。问题在于当你输入 "Doee"
进 findName
函数时会出现。
这个不起眼的拼写错误会导致 undefined
输出,这可能会在后续导致错误,因为没有任何机制阻止我们犯这个错误。
这时 TypeScript 就派上用场了。
如果我们确保 findName
只接受字面的姓氏,即 Craciun, Doe, Pigeon,那么当我们输入 names
数组中不存在的输入(如 “Doee”)时,编译器就会报警。
解决方案
我们已经确定,findName
的有效参数需要包含所有现有的姓氏。为了实现这一点,我们创建一个名为 ExtractSurname
的泛型类型。
ExtractSurname
的代码可能看起来很复杂,但我们将逐步解析它:
type ExtractSurname<T extends string> = T extends `${infer Firstname} ${infer Surname}` ? Surname : null
这里 ExtractSurname
接受一个泛型参数 T
,该参数使用 extends
操作符指的是任何字面字符串。
在 ExtractSurname<"Daniel">
中,T
的值等于 "Daniel"
。
接下来我们应用 TypeScript 的三元操作符,这类似于 JavaScript 的三元操作符,但我们比较的是类型而不是实际数据。
我们知道 names
数组的格式是 “<Name> <Surname>”
,所以这里的 infer
关键字从 T 中提取子类型。
在ExtractSurname<"Daniel Craciun">
中:
infer Firstname = "Daniel" infer Surname = "Craciun"
最后,如果输入满足我们的 “” 格式,则返回姓氏作为类型,否则返回 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]>
names
满足 ExtractSurname 的 “<Name> <Surname>”
格式。
我们使用 as const
将 names
的类型缩小为字面字符串数组。这意味着我们将 names
的类型从 string
强制转换为:
readonly ["Daniel Craciun", "John Doe", "Harry Pigeon"]
参数 (typeof names)[number]
表示 names
中每个索引元素的类型:
"Daniel Craciun" | "John Doe" | "Harry Pigeon"
最后,这是 Surname 的结果类型:
type of Surname: "Craciun" | "Doe" | "Pigeon"
最后一步是更新我们之前定义的 findName
函数,用一个新的 findNameUsingSurname
函数,如下所示:
// 接受实际的 `Surname` 而不是一般的字符串。 const findNameUsingSurname = (surname: Surname) => { // 注意:我们需要后缀操作符 "!" 来断言 `find` 函数不会返回 undefined 值。 return names.find((name) => name.includes(surname))! } // 唯一可接受的输入:"Craciun", "Doe", "Pigeon" = 最大类型安全 const fullName = findNameUsingSurname("Craciun") // 输出: "Daniel Craciun" console.log(fullName)
这就是我们预期的 TypeScript 编译器的魔法:
结论
TypeScript 是一个强大的工具,可以确保代码的类型安全性。通过使用 ExtractSurname
类型,我们能够确保 findName
函数只接受有效的姓氏,从而避免拼写错误和潜在的运行时错误。这个技巧不仅提高了代码的安全性,还增强了开发效率。希望你能在项目中尝试这个技巧,并体验它带来的便利和强大功能。
码云笔记 » 你绝对不能错过的TypeScript技巧