adl-lang / adl

ADL (Algebraic Data Language)
Other
209 stars 22 forks source link

TypeScript: newtype generates type alias and not proper newtype #120

Open bitc opened 3 years ago

bitc commented 3 years ago

Hello it appears that when we use ADL newtype then the TypeScript output is:

type MyNewType = string;

This is not a proper newtype and isn't type safe. TypeScript doesn't have native newtype support but there are various ways to emulate them. (Here is one way: https://github.com/gcanti/newtype-ts)

Thank you

timbod7 commented 3 years ago

I'd definitely like to have a better newtype representation in typescript... but typescript's structural typing makes this hard.

How does newtype-ts work for parameterized types? In many projects, we have an ADL newtype is:

newtype DbKey<T> = String;

which stores a string that is a relational foreign key to a table of type T. Hence DBKey<Customer> and DbKey<Product> are distinct types. Currenty, this gives the desired type safety in every ADL target language except, unfortunately, typescript as you point out.

Are you aware of any approaches to emulating newtypes in typescript that would address this?

bitc commented 3 years ago

Here is the approach I sometimes use. It's ugly but gets the job done. Benefits are that it is very lightweight and standalone, and also has zero runtime cost.

export class DbKey<T> {
    private constructor() {}

    public static wrap<T>(val: string): DbKey<T> {
        return val as any;
    }

    public static unwrap<T>(val: DbKey<T>): string {
        return val as any;
    }

    protected dummy: [DbKey<T>, T] = null as any;
}

export interface Customer {
    name: string;
    address: string;
};

export interface Product {
    id: number;
}

export function test(x: DbKey<Customer>): DbKey<Product> {
    return x;    // error: Type 'DbKey<Customer>' is not assignable to type 'DbKey<Product>'.
}