4 ms·
Closure was also interesting because it integrated type checking and minification, which made minification significantly more useful. With normal Javascript an
by miki123211 2y ago
Closure was also interesting because it integrated type checking and minification, which made minification significantly more useful.
With normal Javascript and typescript, you can't minify property names, so `foo.bar.doSomethingVeryComplicated()` can only be turned into `a.bar.doSomethingVeryComplicated()`, not `a.b.c()`, like with Closure. This is because objects can be indexed by strings. Something like `foo.bar[function]()` is perfectly valid JS, where the value of `function` might come from the user.
A minifier can't guarantee that such expressions won't be used, so it cannot optimize property accesses. Because Closure was a type checker and a minifier at the same time, it could minify the properties declared as private, while leaving the public ones intact.
- wiktor-k 2y ago> Something like `foo.bar[function]()` is perfectly valid JS, A minor thing but `function` is a keyword in JS so technically it's not a "perfectly valid JS".
- mkesper 2y agoOh boy, just think functionName and it fits.
- mananaysiempre 2y ago> A minifier can't guarantee that such expressions won't be used, so it cannot optimize property accesses. Given TypeScript’s type system is unsound, neither could it even if it tried, right? I guess Flow could, but well, here we are.
- afavour 2y agoTheoretically TS could… until it encounters an ‘any’ type in that code path, then it would have to give up. But there are TSconfig options to ensure no use of any so with the right level of strictness it could happen.
- epolanski 2y agoWhat do you mean by unsound exactly. I'm asking because there's no accepted definition of what an unsound type system is. What I often see is that the word unsound is used to mean that a type system can accept types different to what has been declared, and in that case there's nothing unsound about ts since it won't allow you to do so.
- sharlos201068 2y agoThat’s not correct, there’s several ways the actual type of a value differs from what typescript thinks it is. But soundness isn’t a goal of typescript.
- WorldMaker 2y agoIt's maybe useful to note in this discussion for some that "soundness" of a type system is a bit of technical/theoretical jargon that in some cases has specific mathematical definitions and so "unsound" often sounds harsher (connotatively) than it means. The vast majority of type systems are "unsound" for very pragmatic reasons. Developers don't often care to work in a "sound" type systems. Some of the "most sound" type systems we've collectively managed to build are in things like theorem provers and type assertion systems that some of us don't always even consider useful for "real" software development. Typescript is a bit more unsound than most because of the escape hatch `any` and because of the (intentional) disconnect between compiler and runtime environment. Even though "unsound" sounds like a bad thing to be, it's a big part of why Typescript is so successful.
- price 2y agoThere's nothing arcane or particularly theoretical about soundness. It means that if you have an expression of some type, and at runtime the expression evaluates to a value, the value will always be of that type. For example if you have a Java expression of type MyClass, and it gets evaluated, then it must either throw (so that it doesn't produce any value) or produce a value of type MyClass: either an instance of MyClass, or of one of its subclasses, or null. It will never produce an instance of some other class, or an int, or anything else that isn't a valid value for the type MyClass. In addition to helping human readers reason about the code, a sound type system is a big deal for a compiler: it makes it possible to compile the code AOT to fast native code, without inserting a bunch of runtime checks and dynamic dispatching to handle the fact that inevitably some of the types (but you don't know which) are wrong. The compiler implications are what motivated the Dart language's developers to migrate from an unsound to a sound type system a few years ago: https://dart.dev/language/type-system#the-benefits-of-soundness https://dart.dev/language/type-system#the-benefits-of-soundn... so that they could compile Flutter apps AOT. This didn't require anyone to make their code resemble what you'd do in a theorem prover — it just means that, for example, all casts are checked, so that they throw if the value doesn't turn out to have the type the cast wants to return. TypeScript is unsound because when you have an expression with a type, that tells you nothing at all for sure about what the value of the expression can be — it might be of that type, or it might be anything else. It's still valuable because you can maintain a codebase where the types are mostly accurate, and that's enough to help a lot in reading and maintaining the code.
- RyanCavanaugh 2y agoFlow's type system isn't sound either FWIW
- price 2y agoYeah, Flow had the ambition to be sound but has never accomplished it. If you read the Flow codebase and its Git history, you can see that it's not for lack of trying, either — every couple of years there's an ambitious new engineer with a new plan for how to make it happen. But it's a real tough migration problem — it only works if they can provide a credible, appealing migration path to the other engineers across Facebook/Meta's giant JS codebase. Starting from a language like JS with all the dynamic tricks people use there, that's a tough job. (And naturally it'd be even harder if they were trying to get any wider community to migrate, outside their own employer.)
- RyanCavanaugh 2y agoFlow doesn't even check that array access is in-bounds, contrast to TypeScript with noUncheckedIndexedAccess on. They're clearly equally willing to make a few trade-offs for developer convenience (a position I entirely agree with FWIW)
- price 2y agoNeat example, thanks! I hadn't known TS had that option. Array access was actually exactly the example that came to mind for me in a related discussion: https://news.ycombinator.com/item?id=41076755 https://news.ycombinator.com/item?id=41076755 I wonder how widely used that option is. As I said in that other comment, it feels to me like the sort of thing that would produce errors all over the place, and would therefore be a real pain to migrate to. (It'd be just fine if the language semantics were that out-of-bounds array access throws, but that's not the semantics JS has.) I don't have a real empirical sense of that, though.
- kevincox 2y ago> it could minify the properties declared as private, while leaving the public ones intact. I don't think it ever actually did this. It renamed all properties (you could use the index syntax to avoid this) and just used a global mapping to ensure that every source property name was consistently renamed (no matter what type it was on). I don't think type information was ever actually used in minification. So if you had two independent types that had a `getName` function the compiler would always give them the same minified name even though in theory their names could be different because they were fully independent types. The mapping was always bijective. This is suboptimal because short names like `a` could only be used for a single source name, leading to higher entropy names overall. Additionally names from the JS runtime were globally excluded from renaming. So any `.length` property would never be renamed in case it was `[].length`.
- AprilArcus 2y agoof course this created an interoperability nightmare with third party libraries, which irrevocably forked Google's whole JS ecosystem from the community's 20 years ago and turned their codebases into a miserable backwater.
- antifa 2y agoTeaser has a mangle props feature. The mangle props by exact name or pattern has worked for me, but it might affect library objects or browser built-ins that I definitely don't want it to, but I've not gotten the mangle all props of this object version of it to work.