vasia
vasia

Reputation: 1172

Typescript - How to disallow two type aliases that resolve to the same type to be used interchangeably?

Let's say I have two or more type aliases, as such:

declare type A = string;
declare type B = string;

I have variables of these types, as well as functions that operate on them.

const a1: A = "example of a";
const b1: B = "example of b";

function withA(a: A){
    console.log(a);
}

function withB(b: B){
    console.log(b);
}

I would like the following code to error, but it does not:

withA(b1);
withB(a1);

How can I accomplish this? I will also need to be able to initialize the variables with a string (I'm assuming with a cast). However, once initialized I do not want the types to be "implicitly equivalent" and want to have the compiler forbid their interchangeable use.

I also would like to not have to use classes, as described here: TypeScript - specific string types

Upvotes: 12

Views: 1903

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249536

Type aliases as their name suggests don't add anything to the type they alias. So as far as TS is concerned both A and B are the same type namely string.

What you can do is use branded types. This is a technique where you take the base type (string in this case) and intersect it with an object type with a property so as to make the type structurally incompatible with anything else from the compiler's point of view with. The property does not need to exist at runtime, it is just there as a marker for the compiler:

type A = string & { __brand: "A"};
type B = string & { __brand: "B"};

const a1: A = makeA("example of a");
const b1: B =  makeB("example of b");

function makeA(s: string) {
    return s as A
}
function makeB(s: string) {
    return s as B
}
function withA(a: A){
    console.log(a);
}

function withB(b: B){
    console.log(b);
}

withA(b1); // error
withB(a1); // error

The utility functions makeA and makeB are not strictly necessary, you can just use a type assertion when you assign the string, but they make the DX better.

Note: There are two proposals for formalizing this technique in the type system (Structural tag type brands and Nominal unique type brands ) but neither is merged at the time of writing, maybe in a future TS version we will get one of these.

Upvotes: 17

Related Questions