microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.16k stars 12.38k forks source link

Don't allow math operations on different branded numeric types #59423

Open JoshuaKGoldberg opened 1 month ago

JoshuaKGoldberg commented 1 month ago

πŸ”Ž Search Terms

number numeric branded types plus minus add subtract

πŸ•— Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/C4TwDgpgBAwgrgJwRAdgYxAHgCoD4oC8UKcAtgEYQJQBkUA3lAPpNqLLogBcU2UAvgG4AUKEhQAoogD2hWO1QZMAIggzluEWOgBVAMoAROfCSKsyuAGcAJhpHCAZnHTAAltJRQ0AQxQAFRDQAC29LCABJYAhSAAowBFc0CJQpBGkeVOkAGigAd28AGwKIYHCUfQMeCoBKBmEoBqgAeiaoADlZbSgqNIQc8jhgKHC86TgC6ygQgDdoCAAPSDQoyY8IesbkYERPfKKSsoqofHjE5MyRfmEgA

πŸ’» Code

type Currency<T> = number & { __currency: T };
type Euro = Currency<"euro">;
type USD = Currency<"usd">;

function canPurchaseItem(priceInEuro: Euro, walletInUSD: USD) {
    // No type error, but I would have expected one
    return walletInUSD > priceInEuro;
}

πŸ™ Actual behavior

Euro and USD -two different branded numeric types- can be compared with binary operators such as -.

πŸ™‚ Expected behavior

These are two different branded numeric types. I would have expected that binary operations between the two of them would be disallowed.

More technically, if a mathematical binary operation contains:

...then I'd expect to receive a type error.

Additional information about the issue

Per #202, TypeScript doesn't actually formally include a built-in branded/nominal typing. But other mismatched operators are disallowed [playground of mismatched operators]:

// These are all type errors:
console.log([] + []);
console.log([] + {});
console.log([] + 1);
console.log({} + 1);
console.log({} + {});
console.log({ a: 1 } + { b: 2 });
uhyo commented 1 month ago

There is no heuristic that fits all use cases I think. For example, I might argue that Price * TaxRate should be allowed.

To serve this feature request, I think TypeScript needs to support the β€œtrue” nominal typing so that interaction between different types could be made configurable.

jcalz commented 1 month ago

Even with nominal typing you'd need something like #42218 for it to have any effect on mathematical operators.

RGFTheCoder commented 1 month ago

There is no heuristic that fits all use cases I think. For example, I might argue that Price * TaxRate should be allowed.

To serve this feature request, I think TypeScript needs to support the β€œtrue” nominal typing so that interaction between different types could be made configurable.

The matching types are only really required for addition or subtraction.

For example, 5m/s + 3m/s makes sense, but 5m/s + 2 hours has no useful outcome. Multiplication and division will generally create new units: 1$ / 2€ = 0.5 $/€