2024-01-30

Branded Types in Typescript

First I learned that there are multiple type systems. Typescript is structurally typed because of this you might pass wrong arguments at times and do more stupid things due to which people use these “branded” types. Better explanation in the blogs in References and the discussion on Github might be useful as it offers better ways to do this.

function foo(email: string, username: string) {
	// ...
}

// This is perfectly valid but we didn't mean this
const username = "name";
const email = "email";
foo(username, email);
type Brand<T, U> = T & { __brand: U };
type Username = Brand<string, "Username">;
type Email = Brand<string, "Email">;

function foo(email: Email, username: Username) {
	// ...
}

const username = "name";
const email = "email";

// This is not valid now
// foo(username, email);

// You should typecast them using 'as' or
// some function which does that and then pass them

// This is valid
foo(username as Username, email as Email);

function isUsername(username: string): username is Username {
	return true;
}

function isEmail(email: string): email is Email {
	return true;
}

if (isUsername(username) && isEmail(email)) {
	// this is valid
	foo(email, username);
}

References