Logo

dev-resources.site

for different kinds of informations.

Stricter TypeScript >, <, and ===

Published at
10/17/2023
Categories
typescript
javascript
elm
Author
jesterxl
Categories
3 categories in total
typescript
open
javascript
open
elm
open
Author
8 person written this
jesterxl
open
Stricter TypeScript >, <, and ===

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 <.

2 > 1 // true, works with number
{} > {} // false, JavaScript doesn't know
{ valueOf: () => 2} > { valueOf: () => 1 } // true
Enter fullscreen mode Exit fullscreen mode

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:

const proposal1 = #{ name: 'cow' }
const proposal2 = #{ name: 'sup' }
console.log(proposal1 > proposal2)
Enter fullscreen mode Exit fullscreen mode

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:

type Grant = { year: number }
Enter fullscreen mode Exit fullscreen mode

To this and get the type safety for > and < operators:

type Grant = #{ year: number }
Enter fullscreen mode Exit fullscreen mode

An example, if we take the code we have now, sorting an Array of numbers:

someGrantArray.sort( a:number, b:number ) => {
 if(a > b) {
  return -1
 } else if(a < b) {
 return 1
 } else {
 return 0
 }
}
Enter fullscreen mode Exit fullscreen mode

Then later refactor it to take a type alias:

type Grant = { year: number }
Enter fullscreen mode Exit fullscreen mode

Then fix the compiler errors:

someGrantArray.sort( a:Grant, b:Grant ) => {
  if(a > b) {
    return -1
  } else if(a < b) {
    return 1
  } else {
    return 0
  }
}
Enter fullscreen mode Exit fullscreen mode

… 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:

const fund:number = 2019
someGrants.filter ( (year:number)=> year === fund )
Enter fullscreen mode Exit fullscreen mode

Then refactor to use a type alias:

const fund:Grant = { year: 2019 }
someGrants.filter ( (year:Grant) => year === fund )
Enter fullscreen mode Exit fullscreen mode

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:

a = Ok(1)
b = Ok(1)
a == b // false
Enter fullscreen mode Exit fullscreen mode

Whereas, using them in Elm:

a = Ok 1
b = Ok 1
a == b -- True
Enter fullscreen mode Exit fullscreen mode

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?

elm Article's
30 articles in total
Favicon
10 Months of Elm to Angular
Favicon
25 Must-Check Elm Resources for Developers: Tutorials, Tools, and Tips
Favicon
🍃 30 Elm Picks to Fuel Your Functional Code
Favicon
Stateless and stateful components. No! Reusable views in Elm.
Favicon
On why I prefer not to use elm-css
Favicon
GitHub Actions, Devbox, and Elm
Favicon
Run Elm and lunarvim in a devcontainer
Favicon
How to host Browser.application projects
Favicon
How I host Elm web applications with GitHub Pages
Favicon
Announcing my newsletter "Elm with Dwayne"
Favicon
Using ChatGPT o1 to write UI code with Elm
Favicon
Exploring Svelte and Elm: A Comparative Look at Unique Frontend Technologies
Favicon
Exploring Frontend Technologies: Elm vs. Svelte
Favicon
Using a continuation-passing interpreter to make an interactive read operation for the browser with Elm
Favicon
How I use Devbox in my Elm projects
Favicon
Yet Another Tour of an Open-Source Elm SPA
Favicon
Elm 2023, a year in review
Favicon
For lack of a better name, I’m calling it “The Module Pattern”.
Favicon
Exploring the Elm Architecture for Web Applications
Favicon
How to use Elm extensible records
Favicon
Elm with flex and color
Favicon
How I use Nix in my Elm projects
Favicon
Record Type Alias Combinators: A Public Service Announcement
Favicon
Parsing AWS AppSync Responses, Elm GraphQL Libraries, and Only Doing Front-End
Favicon
On continuation-passing style and the factorial function
Favicon
Announcing elm-integer
Favicon
Stricter TypeScript >, <, and ===
Favicon
Final Fantasy Legend Level Editor Update
Favicon
Rebuilding Final Fantasy Legend in Elm
Favicon
Building mobile apps using Elm and Capacitor

Featured ones: