“In the realm of TypeScript, custom types reveal themselves from string arrays like hidden gems.”
“Within the realm of TypeScript, custom types evolve from string arrays as if they were hidden gems.”
TypeScript is a fantastic resource for handling current data and establishing excellent practices.
Today, we’re going to look at the proper technique for extracting a full name from a string array. This will help us achieve a clean and type-safe result.
So without further delay... Let's dive right in... Keeping the same vibe and tonality of the text without altering phrases or HTML tags.
Let's start by taking a closer look at this code and identifying what's wrong with it:.
const names = ["Daniel Craciun", "John Doe", "Harry Pigeon"]
const findName = (surname: string) => {
return names.find((name) => name.includes(surname))
}
// We are allowed to pass in ANY string, which is unideal.
console.log(findName("Craciun")) // Output: Daniel Craciun
console.log(findName("Doee")) // Output: undefined
- This script utilizes an array of
names
for searching purposes. - The method
findName
accepts a stringsurname
and returns the corresponding full name.
The issue occurs when you enter "Doee"
into the findName
function.
This subtle spelling error results in an undefined
output, potentially leading to bugs in the future because nothing is preventing us from making this mistake.
This is where TypeScript comes into play.
If we make sure that findName
only accepts actual Surnames
like Craciun, Doe, and Pigeon, then the compiler should alert us if we try to input something like "Doee"
, which isn’t present in the names
array.
We've determined that the only acceptable arguments for findName
must include all current last names.
To accomplish this, we introduce a generic type named ExtractSurname
.
The code for ExtractSurname
might appear challenging, but let's break it down step-by-step:.
type ExtractSurname<T extends string> =
T extends `${infer Firstname} ${infer Surname}` ? Surname : null
1. In this case, ExtractSurname
uses a type parameter T
, which denotes any literal string
with the help of the extends
operator.
In ExtractSurname<”Daniel”>
, the value represented by T
would be "Daniel"
.
2. Then, we utilize the Typescript ternary operator. It's akin to the JavaScript ternary operator, though we're focusing on comparing types rather than actual data.
3. We understand the structure of our names
array is “ ”, so in this context, the infer
keyword pulls out subtypes from T
.
In ExtractSurname<”Daniel Craciun”>
:.
- Assign the value
“Daniel”
to the variableFirstname
. infer Surname
=“Craciun”
.
4. Finally, if the input conforms to our “ ” format, return the Surname
as a type. Otherwise, return null
.
Alright, now our ExtractSurname
type is set up and ready to go.
We now require a Surname
type to encompass all the surnames contained within 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]>
The format required by ExtractSurname
is “names
.
2. We employ as const
to restrict the type of names
to a collection of specific string literals.
This implies we cast the type of names
from string
to:.
readonly [“Daniel Craciun”, “John Doe”, “Harry Pigeon”]
3. The expression (typeof names)[number]
denotes the type of each individual item within the names
array.
"Daniel Craciun" | "John Doe" | "Harry Pigeon"
4. Ultimately, this is the final type of Surname
:
The final step involves revising our previously defined findName
function by introducing a new function named findNameUsingSurname
as shown below:.
// Takes in an actual `Surname` rather than a general string.
const findNameUsingSurname = (surname: Surname) => {
// NOTE: We need the post-fix operator "!" to assert no
// undefined values are returned from the "find" function.
return names.find((name) => name.includes(surname))!
}
// The only acceptable inputs: "Craciun", "Doe", "Pigeon" = Max Type Safety
const fullName = findNameUsingSurname("Craciun")
// Output: "Daniel Craciun"
console.log(fullName)
And now, the Typescript compiler is doing exactly what we anticipated:.