Stricter TypeScript >, <, and ===
Tags: typescript, javascript, elm
Published: 2023-10-17 15:49:53


---
title: Stricter TypeScript >, <, and === 
published: true
description: TypeScript needs a new feature for Type aliases to allow stricter comparison for >, <, and ===. Possibly for Interface as well, but I don'tt think the demographic that utilizes Interface would care.
tags: TypeScript,JavaScript,Elm
canonical_url: https://jessewarden.com/2023/10/stricter-typescript-and.html
cover_image: 
---

TypeScript needs a new feature for Type aliases to allow stricter comparison for >, <, and ===. Possibly for Interface as well, but I don’t think the demographic that utilizes Interface would care.

When creating Objects, JavaScript needs a valueOf method so it knows what to do with > and <.

```javascript
2 > 1 // true, works with number
{} > {} // false, JavaScript doesn't know
{ valueOf: () => 2} > { valueOf: () => 1 } // true
```
When you’re creating type aliases, you don’t create “methods”; it’s just a Record; it’s data. For example, if you play with the JavaScript Stage 2 Record proposal, this generates a runtime type error that you cannot convert Records to Numbers:

```javascript
const proposal1 = #{ name: 'cow' }
const proposal2 = #{ name: 'sup' }
console.log(proposal1 > proposal2)
```

Whereas, if you used regular Objects in our previous example, it works fine.

If you change an Array.sort method’s compare function or an Array.filter from using a number to an Object, the code will still run, the results will be different because comparing 2 Objects with > and < always results in false. When using TypeScript like I did where you change a number to a type alias, any place in the code that uses those 2 Array methods will “still work”. Thankfully I had unit and acceptance tests that started failing despite TypeScript saying “all is well”.

The simplest way to fix this for > and < is to simply get Record’s to Stage 3:

<https://github.com/tc39/proposal-record-tuple>

… which will in turn get TypeScript to implement them. Based on the possible new Dictionary type (think TypeScript Dictionary === JavaScript Record, TypeScript Record === JavaScript Object):

<https://github.com/microsoft/TypeScript/issues/49243>

… would mean I could convert this:

```typescript
type Grant = { year: number }
```
To this and get the type safety for > and < operators:

```typescript
type Grant = #{ year: number }
```
An example, if we take the code we have now, sorting an Array of numbers:

```typescript
someGrantArray.sort( a:number, b:number ) => {
 if(a > b) {
  return -1
 } else if(a < b) {
 return 1
 } else {
 return 0
 }
}
```
Then later refactor it to take a type alias:

```typescript
type Grant = { year: number }
```
Then fix the compiler errors:

```typescript
someGrantArray.sort( a:Grant, b:Grant ) => {
  if(a > b) {
    return -1
  } else if(a < b) {
    return 1
  } else {
    return 0
  }
}
```
… that compiles, but results in always return 1. If you used Records, it’d at least fail at runtime. One point of TypeScript is to help prevent runtime exceptions via type safety. If you used the suggested Dictionary class in TypeScript, which like Records does not allow methods on the Records, which in turn means you cannot utilize a valueOf which in turn means TypeScript “knows” you can’t convert a Record to a number, which means you’d get a compiler error. That’d be awesome.

However, that doesn’t solve ===. If you have JavaScript comparing numbers:

```typescript
const fund:number = 2019
someGrants.filter ( (year:number)=> year === fund )
```
Then refactor to use a type alias:

```typescript
const fund:Grant = { year: 2019 }
someGrants.filter ( (year:Grant) => year === fund )
```
That code compiles, but is NOT what you want; the intent was “filter by year” not “do these Objects equal each other”. I recognize that equality has it’s own bag of complexity.

For example, using Discriminated Unions in TypeScript:

```typescript
a = Ok(1)
b = Ok(1)
a == b // false
```
Whereas, using them in Elm:

```elm
a = Ok 1
b = Ok 1
a == b -- True
```
The TypeScript way makes sense to those who know JavaScript, or are used to an imperative/OOP language. To FP developers who are used to Sum types, the Elm way makes sense. The imperative/OOP developers think “it’s a unique instance, of course those 2 don’t equal each other, they’re different things, different memory addresses in RAM”. The FP devs are “Why does 1 not equal 1? If I write 1 != 1 in JavaScript, I get false, and if I write 1 == 1, I get true… so what’s going on?”

That’s why a few of the FP libraries that support Discriminated Unions will encourage the use of pattern matching, like “match” rather than rely on operators that weren’t built for that style:

<https://gcanti.github.io/fp-ts/modules/Either.ts.html>

Fine, so FP devs can hide behind match’s they have to build manually; but what about imperative/OOP devs? Maybe a ==== operator or perhaps a compiler setting for “strict comparison” that flags the compiler to either look for valueOf on the interface, or maybe just a linting rule to prevent the use of == & === entirely?