Remix.run Logo
b_e_n_t_o_n 15 hours ago

JavaScript had to implement these checks because you could pass a function literally anything and it was on the implementation to deal with it. Typescript has this same problem tbh.

Spivak 14 hours ago | parent | next [-]

I mean they clearly don't have to, Python has the same problem of being able to pass anything—you don't see this kind of incredibly defensive programming. But Python set a culture of being for "consenting adults" where it's expected that callers will read the docs and ensure preconditions are satisfied, and if they don't whatever happens, happens.

It leads to less code, and more generalizable code. Maybe the caller does know what they're doing, who am I to say they can't.

pcwelder 14 hours ago | parent [-]

>if they don't whatever happens, happens

What happens is you get an error. So you immediately know something is wrong.

Javascript goes the extra mile to avoid throwing errors.

So you've 3>"2" succeeding in Javascript but it's an exception in python. This behavior leads to hard to catch bugs in the former.

Standard operators and methods have runtime type checks in python and that's what examples in the article are replicating.

recursivecaveat 13 hours ago | parent [-]

It's so strange to me that JS goes so far to avoid errors, mashing random stuff into strings or NaNs, misspelled variables just become undefined, etc. Then when your off-the rails program reaches `undefined()` it's "woah woah woah, that's clearly nonsense, stop program execution right this instant". I feel like I could respect it a little more if they committed to "a webpage should never crash under any circumstances" and there were just no exceptions in the language whatsoever.

watwut 12 hours ago | parent [-]

It has nothing to do with the wish to not crash page.

Javascript was a prototype that was supposed to be refined and cleaned. Then management said "we will ship it now" and it was shipped as it was.

At the time, they thought it will be used for small things, minor snippets on web page. Webapps as we have them were not a thing yet.

quotemstr 10 hours ago | parent | prev | next [-]

> you could pass a function literally anything and it was on the implementation to deal with it

So? Language is irrelevant here. What matter is the contract, the agreement that operates through time and space between caller and callee on behavior and expectations. Contracts exist outside of the type system. All a type system can do is enforce a subset of the contractual requirements.

For example, log(0) is undefined. If you write a function to compute log(x), you can (and should) document the function as having unspecified behavior when x is zero.

That's the contract.

The contract holds whether the function is spelled like this

  // x must be positive number
  function log(x) { return ... }
or

  // x must be positive
  function log(x: number): number { ... }
In the second example, we have the type system enforce the "is a number" rule, but at least in TypeScript, you can't use the type system to check that x is positive. In some languages you can, but even in those languages, programs have invariants the type system can't express.

Likewise, you could define a version of log that accepted, e.g. spelled numbers, like log("three"). You can express this weird contract with static or dynamic typing.

The nasty thing is that if early in the log library's history, before the author wrote down the contract or even had TypeScript, someone had written log("three") and gotten a type error, the log library's author might have "fixed" the "bug" by checking for spelled numbers as inputs. That would have been an expansion of the function's contract.

The log library author probably wasn't thinking about the long-term downsides of this expansion. He could have, even when our log() was a pure-JavaScript, just said "The contract is the input is a positive number. Your code is buggy."

But our hypothetical log library author didn't have the experience or good judgement to say no, so we ended up with the kind of "corner case bloat" the article's author describes.

umanwizard 14 hours ago | parent | prev [-]

> Typescript has this same problem

Don’t most languages have the same problem? Even C or Rust have escape hatches that let you override compile-time type checking and pass whatever gibberish you want to a function. How is Typescript any worse?

b_e_n_t_o_n 14 hours ago | parent [-]

node main.js just runs the code. you could simply not run tsc, which plenty of people do. can you ignore all type errors in Rust?

IshKebab 12 hours ago | parent | next [-]

You can ignore the borrow checker at least by using mrustc. And some type checks at least could be optional. The fact that they aren't isn't really a fundamental property of Rust, it's just because it's easy to make them not optional.

In my experience nobody writes Typescript without checking the types. Unlike Python for example where it's not uncommon to have broken types. (And it's way more of a mess in general.)

umanwizard 11 hours ago | parent | prev | next [-]

If you don’t run tsc you are not really using typescript.

nevir 14 hours ago | parent | prev | next [-]

The Rust equivalent is more like using `unsafe` and derefing raw pointers

b_e_n_t_o_n 14 hours ago | parent [-]

Yeah and you can explicitly assert a null is a string in TS, but it's explicit. You can't build a Rust program without those asserts but it's trivial to skip the type checking for TS which is more of a linter than a type system.

chamomeal 14 hours ago | parent [-]

I disagree that TS is more of a linter. But I definitely feel sympathetic to that perspective.

I’ll pull off the cleanest, nicest generic constraints on some component that infers everything perfectly and doesn’t allow invalid inputs, just for a coworker to throw @ts-nocheck on the entire file. It hurts

14 hours ago | parent | prev [-]
[deleted]