Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 712
vasia Avatar asked May 05 '20 17:05

vasia


People also ask

What defines an alias to a type in TypeScript?

In Typescript, Type aliases give a type a new name. They are similar to interfaces in that they can be used to name primitives and any other kinds that you'd have to define by hand otherwise. Aliasing doesn't truly create a new type; instead, it gives that type a new name.

What is @types in TypeScript?

What is a type in TypeScript. In TypeScript, a type is a convenient way to refer to the different properties and functions that a value has. A value is anything that you can assign to a variable e.g., a number, a string, an array, an object, and a function. See the following value: 'Hello'

What is type guard in TypeScript?

A type guard is a TypeScript technique used to get information about the type of a variable, usually within a conditional block. Type guards are regular functions that return a boolean, taking a type and telling TypeScript if it can be narrowed down to something more specific.

What is type assertion in TypeScript?

In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.


1 Answers

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.

like image 93
Titian Cernicova-Dragomir Avatar answered Oct 26 '22 20:10

Titian Cernicova-Dragomir