In TypeScript, the concept closest to a newtype
is to create a new kind of "nominal" type since TypeScript lacks nominal types. One workaround is to use branding techniques where you can create distinct types by adding a label to them.
interface Height {
__brand: "height"
}
function height(inches: number): Height {
return inches as any;
}
function inches(height: Height): number {
return height as any;
}
interface Weight {
__brand: "weight"
}
function weight(pounds: number): Weight {
return pounds as any;
}
function pounds(weight: Weight): number {
return weight as any;
}
const h = height(12); // one foot
const w = weight(2000); // one ton
These new types like Height
and
Weight</code are treated as separate entities by the compiler. Functions like <code>height()
,
inches()
,
weight()
, and
pounds()
act as constructors and accessors for these types with runtime implementations using type assertions.
const h = 12 as any as Height;
const w = 2000 as any as Weight;
By defining distinct named types, you can ensure that you don't mix up different types unintentionally in your code. However, these new types are not interchangeable with regular number types, just like with newtype
. To enforce this distinction, custom functions such as add()
can be used:
type Dimension = Height | Weight;
function add<D extends Dimension>(a: D, b: D): D {
return ((a as any) + (b as any)) as any;
}
The add()
function ensures that only values of the same type can be added together. Overloads can also be used for stricter type checking if needed.
const twoH = add(h, h); // twoH is a Height
const twoW = add(w, w); // twoW is a Weight
const blah = add(h, w); // error, Height and Weight don't mix
To integrate these new types into external functions, specify the expected return type explicitly:
declare function measureScale(): Weight;
var a = height(68);
var b = measureScale();
By enforcing correct type usage, compile-time errors can be caught early and prevent unintended type mixing.
console.log(add(a,b)); // err
console.log(add(a,a)); // okay
Here's a playground link with the code examples provided above.