danvk / effective-typescript

Effective TypeScript 2nd Edition: 83 Specific Ways to Improve Your TypeScript
https://effectivetypescript.com
Other
1.53k stars 226 forks source link

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

Open danvk opened 2 years ago

danvk commented 2 years ago

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

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

danvk commented 2 years ago

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.

danvk commented 2 years ago

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.

danvk commented 2 years ago

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: ```js 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

danvk commented 2 years ago

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

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