At Codesphere, we code mostly in Typescript, not necessarily because it is our favorite language, but because we found out that it made us the most productive.
To start off, here are some benefits of Typescript that make us more efficient:
- ability to code in the same language in both frontend and backend
- (mostly) great OO + types
- asynchronous code
However, recently I came across two very strange behaviors, (I know, they are common in the JavaScript Bubble) and I felt the urge to share them!
1: ['1', '2', '10'].map(parseInt);
I came across this when I wanted to format some user input, convert it into numbers and put them in a chart.
Don't believe me? Open up a console in your browser, paste in the following, and press enter.
['1', '2', '10'].map(parseInt);
This does not work, because map passes three arguments into parseInt()
on each iteration. The second argument index is passed into parseInt as a radix parameter. So each string in the array is parsed using a different radix. '2'
is parsed as radix 1, which results in NaN, '10'
is parsed as radix 2, which is 3, and '1'
is parsed as the default radix 10 because its index 0 is falsy.
2: Inheritance of 'readonly' in Typescript
During a code review at Codesphere, my colleague Roman came across the idea to make methods readonly. What happened next left us a little confused.
It's actually not possible to make a method readonly, but it is possible to make a readonly property with a function type, which has the same effect.
Interestingly enough, it is not possible to assign the property again for instances of the same class, but it is possible to inherit a class and override the property, as well as, assign the property on an instance of a subclass.
class Roman {
readonly jonas: () => void = () => console.log("huh?");
}
class Elias extends Roman {
jonas: () => void = () => console.log("oh no, override works!");
}
const elias = new Elias();
elias.jonas(); // oh no, override works!
elias.jonas = () => console.log("oh no, assignment works too!");
elias.jonas(); // oh no, assignment works too!
That's all for now, I hope that you enjoyed the read! The support for TypeScript is one of the core features of Codesphere IDE. By the way - my name is Saji and I joined the Codesphere team because I love coding and our vision to improve the developer experience.
What is your experience with TypeScript? Feel free to share your story about the things you find confusing in TypeScript!
Top comments (26)
Some teams and projects are finding that they need TypeScript to get out of the way:
TypeScript: JS Doc Reference.
Which is documented on MDN: Array.prototype.map() and MDN: parseInt() (and in the ECMAScript spec Array.prototype.map(), parseInt()) - and I imagine the signatures are revealed by IDEs like Visual Studio Code (which I personally don't use).
Ultimately this isn't a TypeScript issue but a lack of familiarity with the ECMAScript runtime environment.
Which leads to another observation (Here is why you might NOT want to use TypeScript — Part 3: TypeScript is not JavaScript):
Granted Microsoft admits:
It's more pragmatic to not think of TypeScript as a programming language. I think Hongbo Zhang of ReScript said it best:
And given - A Note on Soundness:
i.e. many developers are deriving a false sense of security from TypeScript. So regardless of whether TypeScript is used or not - acquiring JavaScript/ECMAScript competence remains essential.
This is especially true for developers coming from a traditional OO background like Java and C# because going straight to TypeScript leads to a sense of familiarity that is simply not warranted.
... and this illustrates my previous point.
From my perspective I'm confused what you hope to accomplish by making a method
readonly
. From the context I'm guessing you're after Java'sfinal
or C#sealed
.That is not what
readonly
is for - Readonly:Furthermore an object method and a function assigned to an object property are fundamentally different mechanisms which is why the latter can be declared
readonly
and the former cannot.Object methods are typically functions that are assigned to properties on the prototype object that is potentially shared by many object instances that use it as their prototype. It's a reuse mechanism because all these objects share the same functions (i.e. methods) via the prototype. But given that these objects don't actually own the properties that the functions are assigned to there is no way to make them
readonly
.However when a function is assigned to an object's field, it's simply a function value assigned to that particular object. And because it's on the field, it's possible to declare it
readonly
- which constrains it to be assigned in theconstructor
or when the object literal is initially created.And the way the prototype chain works, an object's fields will always be inspected before the prototype object - so function values on objects will always "override" methods on the prototype object - so a
readonly
on the prototype object won't stop an object assigning its own function value.Another thing to be aware of is that arrow functions assigned to public class fields are not accessible via
super
in derived classes (example - because arrow functions bind this to the creating scope - so they can't be used as methods).It should be noted that ECMAScript is object-oriented in the sense that the programmer codes actual objects while in languages like Java and C# the programmer codes classes - i.e. practices class-based object-orientation instead.
ES2015 introduced class:
In a class-based OO language class-membership is static and for the lifetime of the class instance. In ECMAScript a "class" is a creational convenience - once created it is possible to a alter and augment a particular object to fit the specification of another "class" while the identity of the object remains unaffected.
From that point of view I'm deeply concerned when someone is trying to emulate concepts like "final" and "sealed" in ECMAScript/TypeScript because that implies an attempt to practice traditional object-orientation which in my view doesn't align with the spirit of the language.
I see ECMAScript as Function-Oriented and that modules are a far more important mechanism for establishing boundaries than classes.
A lot of ECMAScript programming relies far more heavily on closures compared to traditional OO programming (OOP with Functions in JavaScript) but as they say:
That being said the root cause of the confusion in the article regarding TypeScript/ECMAScript is likely trying to make it into something which it is not.
Regarding your first example here is a good article on this jakearchibald.com/2021/function-ca...
Good one! Thanks for sharing.
cool :) thanks for the tip!
You're trying to mimic something like
final
in, e.g., Java, right? I know I've definitely found myself wishing for that before!I feel like you might be able achieve the runtime behavior you're after with a proxy. You wouldn't get the type safety aspect, though, and I reckon the implementation would be ugly enough that you'd never want to use it.
Instead of
parseInt
Works - and shorter
Bitwise NOT (~):
Unsigned right shift (>>>):
Know the limitations of your tools.
Integers and shift operators in JavaScript
I know all that, but for the purposes of a simple string -> integer parser, this works just fine
The hell did I just read?
that works, but unreadable, therefore it's a bad practice, should be ['1', '2', '10'].map(n => Number.parseInt(n));
dev-to-uploads.s3.amazonaws.com/i/...
:)
Thanks for the article, I started learning typeScript a week ago. And I guess that I am gonna need this article in the next few days :p
Awesome! Don't forget to share your experience. :)
Awesome! Happy to be of help :)
Thanks for the post :)
glad you enjoyed it!
Thanks for your tips.
What a beautiful way to convert string to number..
['1', '2', '10'].map(Number);
It works. If I didn't see your post, I wouldn't know it.
Please observe:
versus
With software the "devil is always in the details".
Martin Fowler
Refactoring: Improving the Design of Existing Code, 2nd Edition p. 10 (2018)
With that in mind when I see
parseInt
in code that I review, I have to conclude that floating point values are not welcome and that the range of values from Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER is considered sufficient for the purpose of the functionality.Ultimately I prefer:
Clearly it's more verbose but the added side benefit is that you aren't at the mercy of the JavaScript engine realizing that it can just reuse the same function - rather than creating a new anonymous function every time
doSomething
is invoked. (JavaScript performance is notoriously unpredictable and inconsistent).In this way I actually find that TypeScript is an impediment to refactoring.
While it seems perfectly capable of inferring a function type when the function is inlined, it needs explicit type declarations once you factor it out (something ReasonML/ReScript doesn't tend to have trouble with) which also means that the source becomes more "noisy".
In my personal opinion inline functions are more imperative because they focus on the "how" of the functionality. With named functions you have an opportunity to be more declarative because the name can relate to the "why".
In pure ECMAScript I prefer:
because I find function declaration hoisting useful - it gives you more freedom of how to order things within a module.
But in TypeScript:
The equivalent to ECMAScript code exists in the "value space" - TypeScript types exist in "type space". With
function toInteger(value: string): number
both are conflated (in the tradition of languages with a C-style syntax).So in TypeScript I often find myself trading-off hoisting:
to separate the "type space" function type
(v: string) => number
from the "value space" definitionconst toInteger = (value) => Number.parseInt(value);
.Thank you! OH Yep,,, I didn't think about floating number.
Anyway, I agree with your opinion.
Thanks again, I learned a lot from your comment.
thanks for the insight! will keep that in mind for floating-point numbers :)
Nice post!
JS/TS are great but they have their legacy..
Looking forward to new TS versions as well as Deno, if it ever gets adopted :)
The way Typescript works with classes was one of the things I found the most interesting about the language when I started learning it. Very cool + interesting article!
Thanks, Tim :) Please do share if you discover anything else cool!
TypeScript takes some getting used to but its quite good once you are used to it :)