danvk / effective-typescript

Effective TypeScript 2nd Edition: 83 Specific Ways to Improve Your TypeScript

Home Page:https://effectivetypescript.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Item 31: Push Null Values to the Perimeter of Your Types

danvk opened this issue · comments

Item 31: Push Null Values to the Perimeter of Your Types

https://effectivetypescript.com/2020/03/24/null-values-to-perimeter/

Comment by Serhii on 2021-04-12 03:36:

From TypeScript perspective it is ok. But I strongly believe that it is not so good for V8 engine - https://v8.dev/blog/react-c... . Unfortunately, there is no good approach. The best way for V8 is to define result as NaN, but from other hand, TS is unable to distinguish NaN and number.

Comment by danvdk on 2021-04-12 09:19:

Interesting article. Reading it, I'd actually guess that pushing null values to the perimeter of your types would be a perf win, since it means no object shapes will ever change. Of course, be wary of speculation about performance wins and losses :) If you've got numbers on this, I'd love to see them.

Comment by Serhii on 2021-04-12 10:54:

I don't have numbers. But some time ago, I made small research in nodejs with help of V8 built ins. Every js object gets a shape (aka interface in TS). When you change the type of this object, V8 makes type transition under the hood. So if you have

let x = null; // HEAP_TYPE
x = 10; // SMI_TYPE

Here we had transition from HEAP_TYPE to SMI_TYPE. It is time consuming.
If you have a lot of transitions in your code - your code will be slower.

Even in this case:

let x = 1;
x = 1.1;

we have a transition from SMI_TYPE to DOUBLE_TYPE, it is also time consuming.
Please keep in mind, transitions are one directional.

When I say - time consuming, I mean it can slow down your code if you have billions of transitions. Of course it does not matter if you have several transitions.

Try to run this code:

class Foo {
    x = 40;
}

class Bar {
    x = 40.1;
}

const foo = new Foo();
foo.x = 43;
const bar = new Bar();
bar.x = 1;

Then, make Heap snapshot in devtools. Then you can find Foo instance and Bar instance. You will see that Bar Retained size is bigger. I believe it is because of transition from int to double

Comment by Serhii on 2021-04-12 10:56:

Btw, I learnt a lot from your book, it is a gem. Thanks you!