Live data from Hacker News

Tricks I wish I knew when I learned TypeScript

cstrnt.dev

71–80 of 276 posts

Re: Tricks I wish I knew when I learned TypeScript

#71
post #65

Everything that TS does JS libraries do better without the horrible tradeoffs ... sadly MS has invested so much into promoting it that it's now almost a requirement for all software development.

> Everything that TS does JS libraries do better

Which ones do you need to do everything TS does?

> without the horrible tradeoffs

Which tradeoffs are horrible?

Re: Tricks I wish I knew when I learned TypeScript

#73

Earlier quoted context omitted.

“Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.” … “For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind…

You still can extend types with type intersections: type A = { id: number } type B = A & { name: string } const b: B = { id: 0, name: 'foo' }

That's creating a new type B. In contrast, interfaces can have their definition spread across multiple code units.

    interface X {
        x(): void;
    }

    interface X {
        y(): void;
    }

    class Y implements X {
        x(): void {
            console.log("hello");
        }

        y(): void {
            console.log("world");
        }
    }

    const z = new Y();
    z.x();
    z.y();

This is important for keeping up with API changes in browsers that may happen faster than the DefinitelyTyped project can keep up.

Re: Tricks I wish I knew when I learned TypeScript

#74

Earlier quoted context omitted.

“Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.” … “For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind…

You still can extend types with type intersections: type A = { id: number } type B = A & { name: string } const b: B = { id: 0, name: 'foo' }

[deleted]

Re: Tricks I wish I knew when I learned TypeScript

#75

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…

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

Re: Tricks I wish I knew when I learned TypeScript

#76

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.

TIL, interfaces can extend classes in TypeScript. [0] If interfaces could not extend classes, that would be a reason to use type programming.

Another reason could be a generic interface. If you have a lifecycle where a type is mutable at one point but immutable at later points, you could use mapped types to enforce those constraints on the class methods generically.

[0]: https://www.typescriptlang.org/play?#code/MYGwhgzhAEAKCmAnCB...

Re: Tricks I wish I knew when I learned TypeScript

#77

Earlier quoted context omitted.

You can declare Sometype|void return type. So the compiler will check that you either don't use the return type or treat it as Sometype. Of course this depends on the logic and for failed routes you should return appropriate results.

void implies that the return type should is undefined behavior and should not be relied upon, so that something like this const x = foo(); is incorrect when foo() returns void. 99% of the time x will be undefined (the value) but there are cases where it would not be. For example arr.forEach(x => x.sort()) sort returns a value as well as having a side effect. But forEach expects a void callback. This code is perfectly…

I agree. You should type something as null if you need to force callers to deal with the null value and can't do that with an exception.

Type it as void if the value isn't really important to the caller, or you'll throw exceptions in an exceptional case.

Common wisdom is to always have user-defined functions return void, but sometimes I think it's okay to use void if you're replacing a built in JavaScript functionality so the outer code was relying on that semantic. For example, replacing a simple usage of findIndex (that returned undefined) with something more complex that does API calls.

Re: Tricks I wish I knew when I learned TypeScript

#78
post #61

Earlier quoted context omitted.

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

This comment would be a lot shorter if you assumed I meant Typescript in response to a Typescript article...

But I digress, the point is in my experience Typescript is very good about catching footguns left around by ECMAScript.

So I'm surprised there isn't some sort of catch for this as written in the article maybe behind a config flag, not by rewriting the definition.

Re: Tricks I wish I knew when I learned TypeScript

#79
post #49

Earlier quoted context omitted.

This isn't a great solution: entry = Object.create(null); entry.name = "Jason"; entry.age = 42; entry.constructor === Object // false - constructor is undefined. `entry` is a valid Human here, but fails your check. Actually, just creating a new class that implements the Human interface will cause a similar problem, since the constructor will be the class instead of Object. You don't even really want to exclude arrays…

I'm having some trouble finding your proposal in the thread... how would you do it?

:) Fair enough:

    function isHuman(obj: unknown): obj is Human {
      return !!obj && 
        typeof obj === 'object' &&
        typeof (obj as any).name === 'string' &&
        typeof (obj as any).age === 'number';
    }
This checks the shape of the object, and returns true if it's a `Human`. This will work for array or objects or classes or anything.

Re: Tricks I wish I knew when I learned TypeScript

#80
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 outsi…

I've always liked the two-nulls solution in JS. `undefined` is a runtime-generated missing value, whereas `null` is a compile-time author-supplied missing value. In other words `undefined` is a "pulled" missing value, `null` a "pushed" missing value. Any feature can be misused, but having the distinction is certainly helpful.
Post reply on HN