Live data from Hacker News

Tricks I wish I knew when I learned TypeScript

cstrnt.dev

81–90 of 276 posts

Re: Tricks I wish I knew when I learned TypeScript

#81

Earlier quoted context omitted.

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

It’s not true that Record will result in a type where any key is valid. If you pass in a primitive like string, then of course any string will be valid. That’s not Record’s fault; what you’re doing is essentially creating an index signature [1]. If you pass a more restrictive type in as the key, it works as expected:

    type Tips = Record;
    const tips: Tips = {}; // error, needs key “foo”
    tips["foo"]; // fine
    tips["bar"]; // error, no key “bar” in tips
It’s worth mentioning that this isn’t just an issue with objects. For example, by default, the index type on arrays is unsafe:

    const arr: number[] = [];
    const first: number = arr[0]; // actually undefined, but typescript allows it
If you do need an index type and want to account for undefined keys, the idiomatic way is the noUncheckedIndexAccess compiler flag [2], which will automatically make any index property access a union with undefined.

[1] https://www.typescriptlang.org/docs/handbook/2/objects.html#...

[2] https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAc...

Re: Tricks I wish I knew when I learned TypeScript

#82

Is there ever a reason to use interface over type? From what I’ve seen it looks like they can both do the same thing but with slight differences in syntax

In my knowledge, there are some differences: - in error reporting: type aliases may be replaced by their definition in error reporting. - you cannot create union types with interfaces - legacy versions of TypeScript does not enable to create recursive type aliases such as type `List = {v: V, right: List | undefined }` - interfaces with same name are merged However the frontier between type aliases and interfaces seem…

Yes the error reporting is why I prefer interfaces over types since a long time now. The definition is useless in many cases

Re: Tricks I wish I knew when I learned TypeScript

#83

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

I recently saw a code example in the VSCode repository where Extract was used in a really awesome way. Say you have a complex class with lots of fields and methods, and you want something that's a specifier for either a constructor's parameter or a query system. Often times, that will be very similar to the underlying class, but just the fields in it - excluding every member that's not a function. Instead of rewriting every single field name for your new type, just have your new type be Exclude or Partial> and the resulting type is perfect for your needs.

Pick is also really useful if you have an interface that passes down a subset of complex things you get from a library; no need to retype their types, just extend a Pick of the library type.

In short, utility types are awesome!

Re: Tricks I wish I knew when I learned TypeScript

#84

Earlier quoted context omitted.

`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 | undef…

You’re right that this is really easy to mess up, especially when defining an array index e.g. const todos: string[] = [“walk dog”]; todos[123].toUpperCase() // error! IMO non constant (as defined by TypeScript) arrays should’ve been automatically assigned a union type with `undefined`, which can also be a fix for Records too: type Tips = Record

You’re looking for the noUncheckedIndexAccess compiler option: https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAc...

Re: Tricks I wish I knew when I learned TypeScript

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

No need to use null for this, undefined works equally well with noImplicitReturns.

For example: https://www.typescriptlang.org/play?#code/LAKAZgrgdgxgLgSwPZ...

Re: Tricks I wish I knew when I learned TypeScript

#86

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

Funnily enough, I have done a session today where I live-coded using the utility types in an effort to explain most of the types listed on that page to a few of my colleagues.

Re: Tricks I wish I knew when I learned TypeScript

#88
post #7

Earlier quoted context omitted.

Note: please post a better solution.

Probably best to just lift one off of a major library like Lodash, they're well tested and efficient (no need to actually use the library, do include the LICENSE somewhere though): https://github.com/lodash/lodash/blob/master/isObject.js But depends on your needs, and you can also attach a typeguard to it.

Why not use the library? With tree shaking and/or direct imports you will ensure the same bundle size as if you just copied the file, and you don't have to worry about licenses etc. In fact, since other dependencies might depend on lodash you can deduplicate the import and actually save on bundle size.

You'll also get notified of any security issues in your lodash imports if your CI pipeline is setup for doing that kind of thing.

Re: Tricks I wish I knew when I learned TypeScript

#89
post #26

I actually don't really like Record types in the way people/library maintainers often use them - the type-checker asserts that values are actually present for all the specified keys, which is fine if the objects with the Record type really were exhaustive; but instead I often see them used where the reality of the data is a Partial - some keys are missing. Something about the abstraction causes people to misuse it fr…

You pretty much always have to define your record type with `| undefined` tacked on to the value type parameter. With that, the problem mostly goes away.

What's the point of typing things if you have to constantly typecheck them anyway?

Re: Tricks I wish I knew when I learned TypeScript

#90

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.

This seems extremely hard to read for me, I would have an hard time trying to understand what it does if I found it in any source code
Post reply on HN