Live data from Hacker News

Tricks I wish I knew when I learned TypeScript

cstrnt.dev

61–70 of 276 posts

Re: Tricks I wish I knew when I learned TypeScript

#61
post #6

Note that `Readonly ` does not prevent a call to side-effect methods when `T` is not among a predefined set of built-in types. Indeed, it prevents such calls only on predefined types such as arrays, maps, and sets. It could be more "accurate" to use `readonly number[]` instead of `Readonly >` for highlighting the difference.

That example disappointed me a little. It was an easy catch because I was told there's an issue, but I'm surprised const arrays don't at least have a warning there. Or even default to having readonly-like behavior

Which "const" do you mean? The one in front of a variable declaration cannot make the array itself constant. That's because that "const" only refers to that variable itself, which is just a pointer (except for the primitive types).

The variable declaration "const" means this variable cannot be changed to point to a different object. It says nothing about the thing it points to and that is how that keyword was designed in this language. It's Javascript (ECMAscript), not Typescript.

On the other hand, using Typescript (which only adds type annotations but the actual code is ECMAscript apart from very few small things such as "enums"), you can append "as const" after an array though as type annotation, as in

    const arr = [1,2,3] as const;

    // Type error: "Property 'push' does not exist on type 'readonly [1, 2, 3]'"
    arr.push(5);
Which is the same as Readonly.

This "as const" annotation can be used for any object, not just for arrays. Of course, it can only guard against known methods of mutating an object, such as direct write access to properties and known mutating function calls for known object types such as the built-in ones (Array, Set, Map, etc., each one needs the definitions for the readonly-version of its type in the Typescript-bundled type library).

Re: Tricks I wish I knew when I learned TypeScript

#62

Earlier quoted context omitted.

Wow, thanks for this. I wasn't even aware of these. I'm surprised I rarely see these in courses/tutorial. These should be like day 1 material.

Typescript is actually a great language. And with those utility types, you can do pretty fun stuff like, for example, you want to mutate a type so that some fields become mandatory: type Ensure = T & { [U in keyof Pick ]-?: T[U] }; class A { foo?: number; bar?: number; baz?: number; } type MandatoryFields = "foo" | "baz"; type B = Ensure ; const b: B = { foo: 42 }; Here, ts will complain that b is missing baz.

Why write code like that, instead of extending the class with a mandatory property? The above code is going to be inscrutable to a lot of engineers, and this isn't something like an ORM where there's a good reason for that.

Re: Tricks I wish I knew when I learned TypeScript

#63
Kinda off topic from someone whos mother tongue is not English: what happened in the past years that people apparently forgot how the irrealis works in English? Shouldn't this be "Things I wish I had known when I learned TS" as opposed to "Things I wish I knew RIGHT NOW"?

Re: Tricks I wish I knew when I learned TypeScript

#64

Utility Types[0] will help you get to the next level on Typescript. It's important to know them and know how and when to use them. [0] https://www.typescriptlang.org/docs/handbook/utility-types.h...

Wow, thanks for this. I wasn't even aware of these. I'm surprised I rarely see these in courses/tutorial. These should be like day 1 material.

Depends on how much you can learn on one day I guess, but TypeScript have lots of features which should be learned before Utility types. For example type parameters, type unions, the role of null and undefined, type assertions etc.

Re: Tricks I wish I knew when I learned TypeScript

#66

Earlier quoted context omitted.

Typescript is actually a great language. And with those utility types, you can do pretty fun stuff like, for example, you want to mutate a type so that some fields become mandatory: type Ensure = T & { [U in keyof Pick ]-?: T[U] }; class A { foo?: number; bar?: number; baz?: number; } type MandatoryFields = "foo" | "baz"; type B = Ensure ; const b: B = { foo: 42 }; Here, ts will complain that b is missing baz.

Why write code like that, instead of extending the class with a mandatory property? The above code is going to be inscrutable to a lot of engineers, and this isn't something like an ORM where there's a good reason for that.

Utility types are useful for example in the React API. You have a "state" defined as a set of properties. Then you have a setState() method where you return the set of properties you want to update, which may be a subset of the full state. So if the type of the component state is TState, then the return type of setState() can be defined as Partial.

Re: Tricks I wish I knew when I learned TypeScript

#67

Kinda off topic from someone whos mother tongue is not English: what happened in the past years that people apparently forgot how the irrealis works in English? Shouldn't this be "Things I wish I had known when I learned TS" as opposed to "Things I wish I knew RIGHT NOW"?

You’re not wrong, but language evolves. What’s convenient in the mouth of native speakers today will usually become grammatically correct eventually.

It would be neat to title blog posts in mock Elizabethan English though:

“Miscellania of Out-Most Significance to the Young Man Who Desireth to Learn the Merveillous Type-Scripte”

Re: Tricks I wish I knew when I learned TypeScript

#68
post #8

Here's another. Instead of returning Sometype|undefined from a function which may or may not have a value to return (such as searchCustomer), return Sometype|null. That forces the function to return a value that's explicitly intended rather than defaulting from a missed out if-else codepath. This is useful since JS is often imperative style code.

The difference between null and undefined in JavaScript is something I wished had never been implemented. Other languages refer to null as their billion dollar mistake, but somehow JavaScript got 2 of them with slightly different but sometime identical behaviour. I would defer to eslint to prevent this particular issue if you care about it, this allows you to set rules in your own code without any impact to the outside world.

I have only seen null vs undefined lead to 2 things in my experience: mistakes and bikeshedding.

Re: Tricks I wish I knew when I learned TypeScript

#69

Earlier quoted context omitted.

Typescript is actually a great language. And with those utility types, you can do pretty fun stuff like, for example, you want to mutate a type so that some fields become mandatory: type Ensure = T & { [U in keyof Pick ]-?: T[U] }; class A { foo?: number; bar?: number; baz?: number; } type MandatoryFields = "foo" | "baz"; type B = Ensure ; const b: B = { foo: 42 }; Here, ts will complain that b is missing baz.

Why write code like that, instead of extending the class with a mandatory property? The above code is going to be inscrutable to a lot of engineers, and this isn't something like an ORM where there's a good reason for that.

A better example is `Partial`, which makes all properties on an interface optional. Lots of use cases for that, like creating a `Dictionary` type that forces you to check for undefined values, or allowing you to support partial-updates to types without having to repeat your interfaces.

The other thing is that types are more often used than read. You don't need to read the `MandatoryFields` type definition often, because your IDE/typechecker will automatically enforce the contract and tell you when you're missing properties.

Re: Tricks I wish I knew when I learned TypeScript

#70

The third example describes something useful in record types, but goes about it in what seems an odd way, and ends up suboptimal as a result. I'd instead use an object type like this: type Human = { name: string; age: number; } which also enforces value types in the compiler, rather than requiring runtime guards.

I found that pattern useful when you don't know what the key will be. I have an iOS app that tracks tips, when you add a tip, it's stored in an object like this: type Tips = { [tipGuid: string]: TipObject } which can be rewritten using Record as type Tips = Record That pattern ins't very useful when creating object with known keys but for data structures where the key is either not known or generated it a godsend.

`Record` is a dangerous type, and I would recommend against it. The problem is that it assumes any key is valid and will return a value type. Example:

    type Tips = Record
    const tips: Tips = {}
    tips["hello"]  // TipObject, but you actually get undefined

It's better to define a Dictionary type like so:

    type Dictionary = Partial>
Then to use:

    type Tips = Dictionary
    const tips: Tips = {}
    tips["hello"]  // TipObject | undefined
That way, you're always forced to check for existence, and you never accidentally attempt to access properties on `undefined`.
Post reply on HN