Creating two number-like types in TypeScript that are incompatible with each other can be achieved by defining two

I've been grappling with the challenge of establishing two number-like/integer types in TypeScript that are mutually incompatible.

For instance, consider the following code snippet where height and weight are both represented as number-like types. However, combining them or treating them as equivalent is illogical and should result in an error:


type height = number; // inches
type weight = number; // pounds
var a: height = 68;
var b: weight = operateScale(); // Call to external function returning weight (non-TypeScript).
console.log(a+b); // This should produce an error.

Is there a method to define two numeric types that are distinct from each other?


EDIT: As noted in the comments, this behavior resembles Haskell's newtype functionality.


EDIT2: After spending hours troubleshooting the issue, I finally arrived at a solution which I have shared below.

Answer №1

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.

Answer №2

After spending countless hours trying to figure it out, I finally came up with a solution:

class length extends Number {}
class width extends Number {}

By extending the Number class, Typescript provides the ability to create unique numeric data types.

You can then proceed to use the variables as demonstrated above.

var x: length = 35;
var y: width = 70;
console.log(x+y); // This should result in an error.

The problem arises when attempting this operation:

console.log(x+x); // This should NOT produce an error.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Breaking apart a combination of unions into a collective group of tuples within TypeScript

Looking for a solution similar to TypeScript extract union type from tuple, this question delves into how to split ['a' | 'b', number] into ['a', number] | ['b', number] rather than focusing on why they are not direc ...

Transform data into JSON format using the stringify method

I am facing an issue with my TypeScript code where I need to retrieve specific information from a response. Specifically, I want to output the values of internalCompanyCode and timestamp. The Problem: An error is occurring due to an implicit 'any&apo ...

The selected image should change its border color, while clicking on another image within the same div should deselect the previous image

I could really use some assistance! I've been working on Angular8 and I came across an image that shows how all the div elements are being selected when clicking on an image. Instead of just removing the border effect from the previous image, it only ...

Updating the background color using typescript

Before transitioning to angular development, I had experience working with vanilla Javascript. I encountered a challenge when trying to modify the css properties of specific elements using Typescript. Unfortunately, the traditional approach used in Javascr ...

The practice of following the UpperCamelCase convention post object transformation

I encountered a situation where I have an object that returned the result from an RxJs subscribe method: result: any { message: null role: Object success: true } To better manage this object in TypeScript, I decided to convert it to a type ca ...

Unable to locate dependencies while testing the react package locally

Recently, I came across this npm package designed for React using Typescript. To debug it locally, I initiated npm link in a new React project but encountered an error: I suspect it may not be reading the packages correctly, but I'm unsure how to re ...

The assigned type does not match the type 'IntrinsicAttributes & { children?: ReactNode; }'. This property is not assignable

I have been struggling to resolve this issue, but unfortunately, I have not found a successful solution yet. The error message I am encountering is: Type '{ mailData: mailSendProps; }' is causing an issue as it is not compatible with type &apos ...

An error in Typescript is indicating that a semicolon is expected. The identifier 'EventNameString' is currently being used as a value, even though it only refers to a type

I've been working on integrating Firebase phone authentication into an older Ionic project and have followed several tutorials. I was able to successfully implement it, but whenever I run ionic serve -l, I encounter the following error: Interestingly ...

Ways to conceal a component based on a specific condition?

In my Angular 8 application, I need to dynamically hide a component based on a specific condition. The condition I want to check is: "status === EcheqSubmissionStatus.EXPIRED" Initially, I attempted the following approach: EcheqProcessComponent templat ...

The issue of losing context when using Papaparse with an Angular 4 function

Check out this block of code httpcsv2Array(event) { var gethttpcsv = Papa.parse('https://docs.google.com/spreadsheets/d/e/yadyada/pub?output=csv', { download: true, header: true, ...

Show JSON information in an angular-data-table

I am trying to showcase the following JSON dataset within an angular-data-table {"_links":{"self":[{"href":"http://uni/api/v1/cycle1"},{"href":"http://uni/api/v1/cycle2"},{"href":"http://uni/api/v1/cycle3"}]}} This is what I have written so far in my cod ...

When defining a stripe in TypeScript using process.env.STRIPE_SECRET_KEY, an error of "string | undefined" is encountered

Every time I attempt to create a new stripe object, I encounter the error message "Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string& ...

What is the best way to update typings.json and typing files?

Here is the structure of my typings.json: { "globalDependencies": { "aws-sdk": "registry:dt/aws-sdk#0.0.0+20160606153210" }, "dependencies": { "lodash": "registry:npm/lodash#4.0.0+20160416211519" } } Currently, I find it tedious to update ...

When using TypeScript, my sorting function returns a value of 0 for all time values

I have been trying to sort this JSON data by date using the provided code, but it does not seem to work as expected. Below is a snippet of my JSON: { "StatusCode":0, "StatusMessage":"OK", "StatusDescription":[ { "id":"1", ...

Raycasting in Three.js is ineffective on an object in motion

Working on a project that combines three.js and typescript, I encountered an issue while attempting to color a sphere by raycasting to it. The problem arises when the object moves - the raycast doesn't seem to acknowledge the new position of the objec ...

What is the best way to access the "document" in a vscode webview provider?

I am currently working on developing an extension for vscode that utilizes React for the interface. However, I have encountered an issue where I am unable to insert my react root component into the HTML of the webview. Despite trying various approaches, n ...

Populating the DOM with a mix of strings and HTMLDivElements by iterating through an array using *ngFor

I have a specific layout requirement that needs to resemble this example: https://i.sstatic.net/4kP2q.png The desired layout should utilize CSS properties like display: grid; someFunction(data) { this.data = data; ...

Jest | Testing Tool for Functions with Multiple Parameters

I have developed a new tool that requires 3 parameters: value (string), service (any - an actual GET service), and name (string). Below is the code for the tool: import { serverErrorResponseUtil } from 'util/serverErrorResponseUtil'; import { H ...

Utilize Angular's Reactive Form feature to track changes in Form Control instances within a Form Array and calculate the total value dynamically

I am currently utilizing Angular Reactive Forms to loop through an array of values and I want to include a total field after the Form Array that automatically updates whenever there are changes in the Form Array control values. Here is some sample data: ...

unable to retrieve information from the redis cache

Attempting to retrieve data from cache using the readData function in the controller file. Encountering an issue where the someVal variable is initially undefined after calling readData, but eventually gets populated with data after receiving a callback ...