Live data from Hacker News

`satisfies` is my favorite TypeScript keyword (2024)

sjer.red

121–130 of 217 posts

Re: `satisfies` is my favorite TypeScript keyword (2024)

#121
post #54

Why? why make your code so complex you even hit this problem. Just use the type: const x: Thetype = .... I am not keen on as const either. Just program to interfaces. It is a better way to think IMO.

The author gets into that. `Thetype` might be complex. It also protects you from overgeneralizing, like casting to and from `unknown` to escape the type checker. type Current = { kind: "ac" | "dc"; amps: number; } type Dc = { kind: "dc"; amps: number; } const ac: Current = { kind: "ac", amps: 10000000, } const handleDc = (thing: Dc) => {} const badConvert = (c: Current) => ({...c, kind: "dc"}); /** * Argument of type…

The bad convert is actually wrong. It should be refactored to an equality check and throw an error if account kind is not "dc". The compiler is correct and its not a good idea to work around this issue.

Re: `satisfies` is my favorite TypeScript keyword (2024)

#122
post #24

Earlier quoted context omitted.

> Hell, ask someone to write a signature for array flat, you'd be surprised how many would fail. To be clear, an array flat type: type FlatArr = Arg extends [infer First, ...(infer Rest)] ? First extends unknown[] ? [...First, ...FlatArr ] : [First, ...FlatArr ] : []; is far from basic Typescript. The average Typescript dev likely doesn't need to understand recursive conditional types. It's a level of typescript one…

If I saw that in a PR I would push very hard to reject; something like that is a maintenance burden that probably isn’t worth the cost, and I’ve been the most hardcore about types and TypeScript of anyone of any team I’ve been on in the past decade or so. Now, that said, I probably would want to be friends with that dev. Unless they had an AI generate it, in which case the sin is doubled.

I’d say it depends. I always advocate for code that is easy to read and to understand, but in extremely rare conditions, hard to read code is the better solution.

Especially when it comes to signatures in Typescript, complex signatures can be used to create simple and ergonomic APIs.

But anyway you shouldn’t be allowed to push anything like this without multiple lines of comments documenting the thing. Unreadable code can be balanced with good documentation but I rarely saw this unfortunately.

Re: `satisfies` is my favorite TypeScript keyword (2024)

#123
post #71

Earlier quoted context omitted.

I generally do this via a `throw UnsupportedValueError(value)`, where the exception constructor only accepts a `never`. That way I have both a compile time check as well as an error at runtime, if anything weird happens and there's an unexpected value.

The fact that there can be runtime type errors that were proven impossible at compile time is why I will never enjoy TypeScript.

Agree wholeheartedly.

Writing TypeScript is better than JavaScript, but the lack of runtime protection is fairly problematic.

However, there are libraries such as https://zod.dev, and you can adopt patterns for your interfaces and there's already a large community that does this.

Re: `satisfies` is my favorite TypeScript keyword (2024)

#124

Earlier quoted context omitted.

Casting? Not really - i think you’d only need a couple type checks. Imo this is mostly useful for situations where you want to handle input validation (and errors) in the UI code and this function lives far away from ui code. Your point about clamping makes sense, and it’s probably worth doing that anyway, but without it being encoded in the type you have to communicate how the function is intended to be used some ot…

How would you convert a Number type to a ClampedNumber type without casting?

Ah, yeah you’re right. I somehow thought typescript could do type narrowing based on checks - like say:

If (i >= 1) { // i’s type now includes >= 1 }

But that is not the case, so you’d need a single cast to make it work (from number to ClampedNumber) or however exactly you’d want to express this.

Tbf having looked more closely into how typescript handles number range types, I don’t think I would ever use them. Not very expressive or clear. I think I hallucinated something closer to what is in this proposal: https://github.com/microsoft/TypeScript/issues/43505

I still think that the general idea of communicating what acceptable input is via the type system is a good one. But the specifics of doing that with numbers isn’t great in typescript yet.

Re: `satisfies` is my favorite TypeScript keyword (2024)

#125
post #68

Earlier quoted context omitted.

We don't have to deal in hypotheticals - we have a concrete example here. There's a method, array.flat() that does a thing that we can correctly describe in TypeScript's type system. You say you would reject those correct types, but for what alternative? It's hugely beneficial to library users to automatically get correctly type return values from functions without having to do error-prone casts. I would always take…

There's nothing I can do about the standard JavaScript library, but in terms of code I have influence over, I very simply would not write a difficult-to-type method like Array.prototype.flat(), if I could help it. That's what I mean by an XY Problem - why are we writing this difficult-to-type method in the first place and what can we do instead? Let's suppose Array.prototype.flat() wasn't in the standard library, whi…

[deleted]

Re: `satisfies` is my favorite TypeScript keyword (2024)

#126

Earlier quoted context omitted.

The satisfies keyword is quite different than "as const." What it does is: 1. Enforce that a value adheres to a specific type 2. But, doesn't cause the value to be cast to that type. For example, if you have a Rect type like: type Rect = { w: number, h: number } You might want to enforce that some value satisfies Rect properties... But also allow it to have others. For example: const a = { x: 0, y: 0, w: 5, h: 5 }; I…

This was a fantastic writeup, thanks. If you don't mind an additional question... How does this work, function coolPeopleOnly(person: Person & { isCool: true }) { // only cool people can enter here } const person = { name: "Jerred", isCool: true, } satisfies Person; coolPeopleOnly(person); Since - person isn't const, so person.isCool could be mutated - coolPeopleOnly requires that it's input mean not only Person, but…

If you ignore the `satisfies` for a moment, the type of `person` is the literal object type that you've written (so in this case, { "person": string, isCool: true }). So coolPeopleOnly(person) works, regardless of whether `satisfies` is there, because TypeScript sees an object literal that has all the person attributes and also `isCool: true`.

(You could mutate it to `isCool: false` later, but then TypeScript would complain because `isCool: false` is different to `isCool: true`. When that happens isn't always obvious, TypeScript uses a bunch of heuristics to decide when to narrow a type down to the literal value (e.g. `true` or `"Jerred"`), vs when to keep it as the more general type (e.g. `boolean` or `string`).)

What `satisfies` is doing here is adding an extra note to the compiler that says "don't change the type of `person` at all, keep it how it is, _but_ also raise an error if that type doesn't match this other type".

(This is only partially true, I believe `satisfies` does affect the heuristics I mentioned above, in that Typescript treats it a little bit like `as const` and narrows types down to their smallest value. But I forget the details of exactly how that works.)

So the `coolPeopleOnly` check will pass because the `person` literal has all the right attributes, but also we'll get an error on the literal itself if we forget an attribute that's necessary for the `Person` type.

Re: `satisfies` is my favorite TypeScript keyword (2024)

#127

Earlier quoted context omitted.

That scenario is usually either misuse of escape hatches (especially at API boundaries) or a misunderstanding of what Typescript actually guarantees.

Not really, I provided these examples a couple weeks ago on another HN thread. TypeScript is simply unsound. https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAllApg... https://www.typescriptlang.org/play/?#code/DYUwLgBAHgXBB2BXA...

Perfect examples of the kind of thing I'm talking about, thank you.

Re: `satisfies` is my favorite TypeScript keyword (2024)

#128

Earlier quoted context omitted.

The fact that there can be runtime type errors that were proven impossible at compile time is why I will never enjoy TypeScript.

Isn't that not necessarily out of the ordinary though? What if there's a cosmic ray that change's the value to something not expected by the exhaustive switch? Or more likely, what if an update to a dynamic library adds another value to that enum (or whatever)? What some languages do is add an implicit default case. It's what Java does, at least: https://openjdk.org/jeps/361

> What if there's a cosmic ray that change's the value to something not expected by the exhaustive switch?

I could forgive that.

The TypeScript case is more like "what if instead of checking the types we just actually don't check the types?".

Re: `satisfies` is my favorite TypeScript keyword (2024)

#129
post #68

Earlier quoted context omitted.

We don't have to deal in hypotheticals - we have a concrete example here. There's a method, array.flat() that does a thing that we can correctly describe in TypeScript's type system. You say you would reject those correct types, but for what alternative? It's hugely beneficial to library users to automatically get correctly type return values from functions without having to do error-prone casts. I would always take…

There's nothing I can do about the standard JavaScript library, but in terms of code I have influence over, I very simply would not write a difficult-to-type method like Array.prototype.flat(), if I could help it. That's what I mean by an XY Problem - why are we writing this difficult-to-type method in the first place and what can we do instead? Let's suppose Array.prototype.flat() wasn't in the standard library, whi…

> Suddenly this typing problem goes away, because the type of your "flatten" method is just "MyStructure -> [MyElements]".

How is that less maintenance burden than a simple Flatten type? Now you have to construct and likely unwrap the types as needed.

And how will you ensure that you're flattening your unneeded type anyways? Sure you can remove the generics for a concrete type but that won't simplify the type.

It's simple. It's just recursive flattening an array in 4 lines. Unlikely to ever change, unlike the 638255 types that you'd have to introduce and maintain for no reason.

There are many reasons not to do that. Say your business logic changes and your type no longer needs one of the alternatives: you are unlikely to notice because it will typecheck even if never constructed and you will have to deal with that unused code path until you realize it's unused (if you ever do).

You made code harder to maintain and more complex for some misguided sense of simplicity.

Re: `satisfies` is my favorite TypeScript keyword (2024)

#130
post #71

Earlier quoted context omitted.

I generally do this via a `throw UnsupportedValueError(value)`, where the exception constructor only accepts a `never`. That way I have both a compile time check as well as an error at runtime, if anything weird happens and there's an unexpected value.

The fact that there can be runtime type errors that were proven impossible at compile time is why I will never enjoy TypeScript.

If Typescript is javascript with types bolted on, Rescript is javascript with types the way it should have been. Sound types with low complexity. https://rescript-lang.org/
Post reply on HN