Live data from Hacker News

Abuse of the nullish coalescing operator in JS/TS

fredrikmalmo.com

41–50 of 70 posts

Re: Abuse of the nullish coalescing operator in JS/TS

#41

In general, I agree. You don’t want silent failures. They’re awful and hard to reason about. > By doing this, you're opening up for the possibility of showing a UI where the name is "". Is that really a valid state for the UI? But as a user, if I get a white screen of death instead of your program saying “Hi, , you have 3 videos on your watchlist” I am going to flip out. Programmers know this so they do that so that…

> But as a user, if I get a white screen of death

No one suggests hard crashing the app in a way that makes the screen white. There are better ways. At least, send the error log to telemetry.

Re: Abuse of the nullish coalescing operator in JS/TS

#42

It seems Rust's unwrap is the exact opposite of ?? "". It throws an error instead of using a fallback value, which is exactly what the author suggests instead of using ?? "".

Yes that was a mistake. unwrap() is the error. unwrap_or() is the fallback.

Also unwrap_or_default() which is useful in many cases. For example the default for a string is empty string, default for integers is 0 and default for bool is false.

For your own types you can implement the Default trait to tell Rust what the default value is for that type.

Re: Abuse of the nullish coalescing operator in JS/TS

#43
post #9

Earlier quoted context omitted.

Yes it should, because hopefully errors are logged and reported and can be acted upon. Missing name doesn’t.

This reads like a dogmatic view of someone who hasn’t worked on a project that’s a million plus lines of code where something is always going wrong, and crashing the entire program when that’s the case is simply unacceptable.

> something is always going wrong

I hate this sentence with a passion, yet it is so so true. Especially in distributed systems, gotta live with it.

Re: Abuse of the nullish coalescing operator in JS/TS

#44

I keep wondering about a type system where you can say something like "A number greater than 4" or "A string of length greater than 0" or "A number greater than the value of $othernum". If you could do that, you could push so much of this "coping" logic to only the very edge of your application that validates inputs, and then proceed with lovely typesafe values.

You can do it in typescript with branded types: https://effect.website/docs/schema/advanced-usage/#branded-t... There is some ceremony around it, but when you do the basic plumbing it's invaluable to import NonEmptyString100 schema to define a string between 1 and 100 chars, and have parsing and error handling for free anywhere, from your APIs to your forms. This also implies that you cannot pass any string to an API…

while this is nice, the type itself doesn't encode the logic (unlike refinement type)

i think this would be really nice if validation libraries like zod returned branded types when they are validating non-comp-time types (like z.ipv4() should return some IPv4 branded type)

Re: Abuse of the nullish coalescing operator in JS/TS

#45
post #44

Earlier quoted context omitted.

You can do it in typescript with branded types: https://effect.website/docs/schema/advanced-usage/#branded-t... There is some ceremony around it, but when you do the basic plumbing it's invaluable to import NonEmptyString100 schema to define a string between 1 and 100 chars, and have parsing and error handling for free anywhere, from your APIs to your forms. This also implies that you cannot pass any string to an API…

while this is nice, the type itself doesn't encode the logic (unlike refinement type) i think this would be really nice if validation libraries like zod returned branded types when they are validating non-comp-time types (like z.ipv4() should return some IPv4 branded type)

The type encodes the logic in the schema, it is absolutely a refinement as every parser is. Maybe you meant a comparison with dependent types?

Now every time you will have to use a NonEmptyString255 as a type it has to be branded by passing through the constructor, so you can't pass a normal string to an API expecting it, and you get the error at type level. The logic is encoded in the schema itself, which you can click.

And it also provided the decoder (parser) and encoder (constructor). So you use the parser in a form or whatever and get parsing and precise errors (for it being too long, too short, not a string). And you can annotate the errors in any language you want too (German, Spanish, etc, English is the default)

Essentially this approach is similar to using some class NonEmptyString without using a class and while keeping the information at type level.

It's practical and the ceremony goes as far as copy pasting or providing a different refinement, besides, AI can write those with ease and you don't need to do it frequently, but it's nice in many places not mixing UserIDs with ProductID or any other string makes codebases much easier to follow and provides lots of invariants.

Re: Abuse of the nullish coalescing operator in JS/TS

#46

I can see this. I learned from a friend to use Zod to check for process.env. I refined it a bit and got: ``` const EnvSchema = z.object({ NODE_ENV: z.enum(['development', 'production', 'staging']), DATABASE_URL: z.string(), POSTHOG_KEY: z.string(), }); export type AlertDownEnv = z.infer ; export function getEnvironments(env: Record ): AlertDownEnv { return EnvSchema.parse(env); } ``` Then you can: ``` const env = get…

I'm not familiar with Zod, but one thing that is quite important on the user end is to produce multiple (but, per policy, not infinite) error messages before giving up. That is, list all environment variables that need to be set, not just whichever one the code happens to be first.

This could be implemented with `??`, something like: `process.env.NODE_ENV ?? deferred_error(/temporary fallback/'', 'NODE_ENV not set')`, but is probably best done via a dedicated wrapper.

Re: Abuse of the nullish coalescing operator in JS/TS

#47
post #6

Should throw expressions ( https://github.com/tc39/proposal-throw-expressions ) ever make it into the JavaScript standard, the example could be simplified to: const env_var = process.env.MY_ENV_VAR ?? throw new Error("MY_ENV_VAR is not set");

so like assert(process.env.MY_ENV_VAR) but in a less readable oneliner?

Assuming assert existed, it would almost certainly be judging its value for being falsy, while the ?? operator judges its LHS for being nullish, which is a narrower category. For strings, this affects whether the empty string is acceptable or not.

Re: Abuse of the nullish coalescing operator in JS/TS

#48
post #26

Early errors are good, but I think the author overstates the importance of filtering out empty strings --- I disagree that erroring out when the app doesn't have ALL the data is the best course of action. I imagine it depends a bit on the domain, but for most apps I've worked on it's better to show someone partial or empty data than an error. Often the decision of what to do when the data is missing is best left up t…

Sure, but in that case string | undefined is the correct type, and turning it into string | “” isn’t helping anybody.

It’s the difference between:

if (fooBarDisplayName) { show div }

And:

if (foobarDisplayName && foobarDisplayName.length > 0) { show div }

Ultimately, type systems aren’t telling you what types you have to use where - they’re giving you tools to define and constrain what types are ok.

Re: Abuse of the nullish coalescing operator in JS/TS

#49
The problem I have when writing JS/TS is that asserting non-null adds verbosity:

Consider the author’s proposed alternative to ‘process.env.MY_ENV_VAR ?? “”’:

    if (process.env.MY_ENV_VAR === undefined) {
     throw new Error("MY_ENV_VAR is not set")
    }
That’s 3 lines and can’t be in expression position.

There’s a terser way: define an ‘unwrap’ function, used like ‘unwrap(process.env.MY_ENV_VAR)’. But it’s still verbose and doesn’t compose (Rust’s ‘unwrap’ is also criticized for its verbosity).

TypeScript’s ‘!’ would work, except that operator assumes the type is non-null, i.e. it’s (yet another TypeScript coercion that) isn’t actually checked at runtime so is discouraged. Swift and Kotlin even have a similar operator that is checked. At least it’s just syntax so easy to lint…

Post reply on HN