Extracting Custom Types from String Arrays in TypeScript: A Step-by-Step Guide

6 min read
May 19, 2024
A Person that has reached artificial intelligence enlightenment.
This image has been generated by AI
“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 string surname 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 variable Firstname.
  • 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 “ ”, and this is followed by 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:

type of surname: “Craciun”, “Doe”, “Pigeon”

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:.

Here is the Typescript compiler doing its magic

Read more in Tech