Hacker News new | ask | show | jobs
by joenot443 1198 days ago
Well written and super funny. Reminded me a bit of Scott’s writing, particularly the descriptions of the horrified interviewer.

I’ve never worked in a Typescript shop, is there any truth to the satire here? The sea of confusing types to solve any problem?

8 comments

I’d say generally the opposite is true. Most commercial software I’ve worked on typically contained only simple typing—discriminated unions are about as complex as it gets—and it’s more of a problem that people get lazy and start using “any” too much than they go overboard.

Where complex types can be a problem is when working with open source libraries, especially when the types are community-developed, separate to the library itself. The library API may not be particularly amenable to easy typing, and the community types can end up being rather confusing, especially to people who developed neither the types nor the original library.

I tell my fellow developers at work: "Any is banned. If you want any, use JavaScript, and we don't use JavaScript here. Perhaps you haven't heard about unknown?"

In my experience, 90% of the time when a developer uses any, they just don't know about unknown. 9% it's because they are lazy. 1% is because you are implementing something from an imported library, and they fell into the other 99%.

I make an exception for using any in type params which extend type params, eg

  const foo = <T extends Record<string, any>>(dict: T) => …
This is a good signal that foo maps over dict in some generic way that cares more about its dictionary-ness than its values. Sure, unknown works in that position too, but at least IMO the “doesn’t care” bit is more informative than “doesn’t know”. The latter might imply more type narrowing will happen than is the case.
The problem with that is that when consuming of the dictionary, “doesn’t know” is actually more appropriate. If you then access Object.values(foo) in your method you are given an iterable of anys which is unsafe.
If the function is doing something with the values which is unsafe, sure. My point was the more relaxed constrain on the type signature can be used to imply it’s only concerned with the dictionary’s keys.
Coming back to this after the other thread cooled down a bit lol - to me, unknown actually implies that the function doesn’t care about the values more than any, as the compiler will enforce that they’re unused.

And in any case, I’d almost always lean towards the option with stronger type safety guarantees. Especially in a team environment when someone else may be modifying your code later. As a convention, I almost never use any.

Then use unknown. Either you know what's in the dictionary and can type it or you don't. Stop being lazy.
Any doesn't mean "doesn't care". Any means "YOLO, do whatever you want, I'm one of those cool parents who'll let you smoke and drink beer."
What is the distinction?
The distinction is just because you "don't care" about the values right now, nothing stops the next developer (including future you) from needing the values.

So now you've gone from not caring to "enabling someone to shoot themselves in the foot" if they don't read the types of the parameters carefully. That's the difference.

Use "unknown" and TS complains that you're treating something as an object.

Use Object.defineProperties and TS complains because that stuff is invisible to it after how many years?

I think you're right, of course, but TS is hardly perfect and treating its ways as gospel is not an improvement over JS. The "right ways" change over time and beliefs are not shared among everyone.

If you know what's in the object cast it as that type or be sure by saying if ('propertyName' in unknownObject).

TS is far from perfect. These aren't its ways (it provides any, so of course it's fine with it). These are my restrictions: if you're using a type system, actually use it. Don't lie to yourself and throw anys in your code.

any is the last resort.

How do you get around properties assigned via Object.defineProperties recognised by TS, though? I really don't know. Is is an unrelated question

Here's how I would do it:

    interface IOptionsX {
        x: number;
    }
    
    interface IOptionsY {
        y: number;
    }
    
    const testObj: IOptionsX = {x : 0};
    
    Object.defineProperties(testObj, {
        y: {
            value: 100,
            writable: true
        },
    });
    
    if ('y' in testObj) { // Could also do `&& typeof testObj.y === 'number'` to be VERY sure.
        const testObjWithY = testObj as typeof testObj & IOptionsY;
        console.log(testObjWithY.y)
    }
This is "stunt programming," similar to shooting an arrow into a target from horseback in a rodeo. It has little to do with the real-life business of real-life cowboys.

In this case it's using the type system to calculate the answer to a problem, which is not useful because the type system can't output anything to the console or do other IO. The "answer" will only be visible in your IDE.

The type system is powerful and extremely capable because it had to support existing javascript patterns, like "this function takes a parameter that might be a string or might be a number or might be an array" and make them type-safe.

It’s a very beautiful language even despite being compatible with Javascript. The code here is delightfully absurd, please don’t think it is representative.
I’ve worked for TypeScript companies for a while now. Most devs I’ve met are fairly pragmatic and wouldn’t try something like this for production code, but I’ve definitely met a couple who have an exceedingly deep grasp of the type system and would appreciate the whimsy of it.
> The sea of confusing types to solve any problem?

Mostly in typings either provided by the library itself or via the 3rd party DefinitelyTyped project. Some typings have been made so complex, that it is hard to follow what kind of concrete type is exactly expected or allowed.

[1]: https://github.com/DefinitelyTyped/DefinitelyTyped

In my experience, even the more wild/exotic patterns in typescript tend to flatten into something rather readable at their usage site. 95% of the time when I use a library that does anything close to what is done in this article I write my code, hover over it, intellisense tells me its type and I say "wow, how was it able to do that? Cool!" And I carry on.
> is there any truth to the satire here?

As long as you're satisfied with the answer being shown on a tooltip when you hover over a variable... sure.

Notice how the whole type structure ending up as 4 lines of inconsequential javascript after going through the typescript compiler at the very end.

I daresay the forest of types to validate our endpoint input/output is intimidating to many, but this post is on a whole different level.