#CodeSafety

Strengthening Input Validation with Branded Types in TypeScript

804 words, 4 minutes read time.

TypeScript offers numerous advantages in building safer, more reliable code, and one advanced but highly effective approach is branded types. If you work with strings in TypeScript, particularly when validating inputs like email addresses or formatted IDs, you’ve likely encountered the challenge of distinguishing specific formats. Branded types bring a way to enforce stronger validation and add clarity to your TypeScript code without sacrificing flexibility. Inspired by Andrew Burgess’s insights in his video Branded Types Give You Stronger Input Validation, this blog will break down the branded type pattern and its applications.

What Are Branded Types?

In TypeScript, branded types allow you to attach a unique identifier to specific string formats, helping differentiate between regular strings and strings that must adhere to particular formats—like email addresses, product IDs, or dates. Although branded types can work with numbers or other primitives, they’re especially useful with strings.

Imagine a function called sendWelcomeEmail designed to send emails to new users. Normally, we might define the email parameter as a simple string, but this allows any string to be passed, even if it doesn’t resemble a valid email address. Branded types solve this by creating a custom type that isn’t directly assignable from regular strings, requiring validation first.

How to Create a Branded Type in TypeScript

Creating a branded type is relatively simple. Instead of defining a type alias as a basic string, you create a type that’s an intersection of string and an object with a unique “brand” property. Here’s a sample implementation for an EmailAddress branded type:

type EmailAddress = string & { __brand: "EmailAddress" };

Here, EmailAddress is a string with a unique brand that makes it distinct from other strings. The branded type requires validation before assignment, ensuring that any EmailAddress passed around the system has been verified.

Validating Branded Types with Type Guards

To validate branded types, you’ll need a type guard function to check the format of the input string and “brand” it if it passes. In TypeScript, we define this with a function that returns a type predicate. Below is an example function isEmailAddress that checks if a string includes @gmail.com (for simplicity) and returns a branded EmailAddress type if it does.

function isEmailAddress(email: string): email is EmailAddress {    return email.includes("@gmail.com");}

This function validates the email and then “brands” it, letting TypeScript know that we consider this string a genuine EmailAddress. While this example is basic, you could expand it using regex or other validation rules.

Why Use Branded Types?

Branded types provide robust input validation without the need for constant re-validation. By using branded types at the boundaries of your application—such as at the point where user input enters the system—you can validate and brand the inputs, safely passing them around as trusted values. This results in several key benefits:

  1. Increased Reliability: Once a value is branded, you know it has passed specific validation criteria.
  2. Reduced Risk of Bugs: Reduces the chance of invalid data being processed or causing errors down the line.
  3. Code Readability: When you see EmailAddress instead of string, you know instantly what data is being handled.
  4. Enhanced Security: By enforcing validation at the edges of your application, you prevent potentially harmful data from circulating within your system.

Asserting Branded Types

Another way to validate branded types is with assertions. Assertions allow us to throw errors when validation fails, which can be particularly helpful for critical data. Here’s an example of an assertion function for our EmailAddress type:

function assertEmailAddress(email: string): asserts email is EmailAddress {    if (!email.includes("@gmail.com")) {        throw new Error("Invalid email address format");    }}

This function throws an error if the email doesn’t match the expected format. Assertions are especially useful when you need immediate error feedback, such as in input validation for API endpoints or user input.

A Practical Use Case: The SignUp Function

Imagine you’re building a sign-up system where the user provides an email address. Here’s how you might use branded types to ensure that only validated emails are passed to your functions:

function signUp(email: string) {    assertEmailAddress(email);    sendWelcomeEmail(email); // Now considered a validated EmailAddress}

By calling assertEmailAddress early, we ensure that sendWelcomeEmail only receives valid email addresses, adding a layer of safety to our application.

Advantages of TypeScript with Branded Types for Security and Safety

TypeScript’s branded types offer developers the best of both worlds: strong type checking and safe, validated data. With TypeScript’s support for sophisticated type constructs, branded types are especially powerful tools for ensuring that input data meets system expectations.

Using branded types at scale improves both code reliability and security. As noted by Andrew Burgess, “Branded types are a great way to use TypeScript and input validation together to make a stronger, safer system.” This insight underscores the utility of branded types for applications where validated data is essential to system integrity and security.

D. Bryan King

Related Posts

Rate this:

#brandedTypes #codeSafety #dataIntegrity #emailValidation #emailValidationTypeScript #inputValidation #programming #secureCoding #secureInputValidation #strongTyping #typeChecking #TypeScript #TypeScriptAdvancedPatterns #TypeScriptAssertions #TypeScriptBestPractices #TypeScriptBlog #TypeScriptBrandedTypes #typescriptCodingTechniques #TypeScriptDataValidation #TypeScriptEmail #TypeScriptIntersections #TypeScriptPatterns #TypeScriptSecurity #TypeScriptTutorial #TypeScriptTypeGuards #TypeScriptValidation

Ainiriandainiriand
2024-09-16

Option types are a game-changer for handling nullability in code.

With Option<T>, you always get explicit nullability checks, making your code safer and more predictable. No more unexpected null surprises or undefined headaches. Just clean, reliable code. 🚀

Client Info

Server: https://mastodon.social
Version: 2025.04
Repository: https://github.com/cyevgeniy/lmst