4 ms·
TypeScript's biggest flaw, IMO, is that it can't (or chooses not to- I don't understand anything about JS-adjacent build systems and how they work) understand t
by ragnese 5y ago
TypeScript's biggest flaw, IMO, is that it can't (or chooses not to- I don't understand anything about JS-adjacent build systems and how they work) understand the difference between "code I write" and "code someone else wrote".
I'm not going to beat the dead horse of how unsound TypeScript's type system is. We all know that most of that is intentional because it would just be too inconvenient to work with existing JavaScript code (Aside: think about what this implies about existing JavaScript code. This makes alarm bells go off in my head).
However, why should I have to deal with an unsound type system in my new, fresh, TypeScript-only code?
I'm not saying it's simple or criticizing anybody's work, but it would VASTLY increase my opinion of TypeScript if I could enable some kind of 'truly-strict-new-code' flag that would error on unsound code in the current project and maybe just warn when I call unsound code from a dependency.
Because, honestly, I'm truly disappointed that the one language that finally has a chance to unseat JavaScript has such a poor type system that it can't even do basic sub-type variance correctly no matter how many flags you set. Out of all of the statically typed languages I've used, TypeScript has helped me the least when it comes to type errors.
- pcthrowaway 5y agoIs it even possible for the program to know at compile-time if the types will be violated at run-time? Even if you declare data from other sources 'not mine' this still seems like it would be impossible to do.
- danenania 5y agoYou can accomplish it with a library like zod[1]. It's not the prettiest though; first class support would definitely be better. 1 - https://github.com/colinhacks/zod https://github.com/colinhacks/zod
- jayshua 5y agoThe type system can force you to handle both cases - the data being in the expected & typed form, and the data not being in the expected form. You would have a function like this: parseJsonString : String -> Result Error Person Where Result is a value that is either an Error or a Person. To get the Person value out, the type system forces you to also do something with the Error if it exists. Not handling both possibilities in some way is a type error.
- bern4444 5y agoAs long as the data is from within the system, yes. If you're trying to use data from an external source (api request, external json file etc) than no, but that's true of most type systems. If you correctly type the response of that request then the answer becomes yes again.
- pcthrowaway 5y agoMaybe I misunderstood, but I thought the person I was responding to was suggesting having the option for sound typing enforced in typescript, at compile time. Because if it's at run-time... well the run-time is Javascript, so there's of course no typing there. This isn't really on Typescript There are however javascript libraries which allow you specify object schemas and throw errors when mismatches occur. Perhaps the suggestion then is to build some shared layer between the TS compiler and V8 so that similar schemas can be autogenerated by the TS compiler and enforced at run-time.
- rsrsrs86 5y agoThere is generally no typing in run time. One of the practices in writing compilers is to skip type checks during run time, because you know beforehand that no type errors are possible. That is type erasure. If you're interested, lookup existential types. Very interesting stuff.
- ragnese 5y ago> Is it even possible for the program to know at compile-time if the types will be violated at run-time? I mean, that's kind of the whole point of static typing, right? If you're speaking to TypeScript/JavaScript, specifically, then yeah, the compiler can't guarantee anything because someone can hop into the running program and start modifying objects as they please (at least for web page code). But the compiler could still make the weaker guarantee that "the code that I can see is soundly typed". And for 99% of us that's effectively the same thing.
- nesarkvechnep 5y agoTake a look at Elm.
- armchairhacker 5y agoI was just thinking about this. TypeScript’s main flaw is that it’s a full superset of JavaScript, which is a full superset of old JavaScript with weird conventions (null vs undefined, == vs ===). Furthermore, its type system is so strong it can type practically any JavaScript, but at the cost of strong typing. I wish there was language that was basically JavaScript/TypeScript, but it gets rid of backwards-compatible JavaScript edge cases and conventions (like, no more undefined, no more {} == ‘0’, no more weird function ‘this’), removes advanced types and makes the type system sound, and (just in case) can actually check typecasts and non-null assertions at runtime. JavaScript and TypeScript are honestly great languages, but they are great languages with major flaws in the name of backwards-compatibility. Even with strict typing enabled, I write a surprising amount of bugs which ultimately come down to type errors. And not only that but silent type errors, because TypeScript’s casts and not-null-assertions aren’t checked. So often I find that a mistyped or null value managed to slip through a type guard without the compiler saying anything.
- sandruso 5y agoBackward compatibility is bridge to whole JS ecosystem. If there was way use “unsafe” old js code within newer language that would be great. But it would bring another set of annoying problems.
- ragnese 5y agoTotally agree. That's kind of what I was going for when I described the hypothetical "true-strict" mode: don't allow the code I'm writing to do bad/unsafe stuff. And yes, I really wish that TypeScript's non-null assertions actually included a runtime check. Type casts can't really do anything at runtime without a definition of what "being type Foo" actually means, since the types are erased, but you can definitely check if something is null or undefined and throw an error. I think it would be fair to have the non-null assertion do a runtime check, and if you want the non-null assertion with no runtime code, just use a type cast.
- z3t4 5y agoI add a bunch of tests at the top of all JS function - To make sure the parameters are correct. There is no good reason to not write these tests. You can throw friendly errors, and bugs will be detected early.
- ahuth 5y agoI can't get behind the idea that a type system is either completely sound or completely useless. Surely preventing 99% of issues (or 90%, or 80%) is good? I'm also not aware of too many real-world instances of unsoundness in TS that has caused problems. Obviously I don't know your specific scenario, but it's possible there are ways to work with TS (instead of against it) to get a good level of type safety.
- ragnese 5y ago> I can't get behind the idea that a type system is either completely sound or completely useless. EDIT: I'm sorry. I thought you were someone else. [REDACTED]
- ahuth 5y agoCan you link to where we previously discussed TypeScript? I don't remember that, and can't find it anywhere in my comment history (looked back to 2019). Also, I didn't mean to misinterpret what you said. Sorry about that.
- ragnese 5y agoYikes. I'm so sorry. I mistook your username for another one. That was incredibly rude of me to react like that without at least double-checking. I had a long and frustrating back-and-forth with someone else some time ago where I pointed out what I believe to be several glaring issues with TS's type system (like the fact that `readonly` properties on object types don't actually guard against writes in most situations) and every one of my criticisms was met with the equivalent of "You're saying a type system has to be 100% perfect or it's completely useless". And even though it's the internet and I should know better, it just irritated the hell out of me. Again, I'm really sorry for jumping at you like that. I think I'm traumatized over arguing about TypeScript... lol
- ahuth 5y agoNo worries. I made an assumption about what you were saying that wasn't helpful, either. All good!
- WorldMaker 5y ago> TypeScript's biggest flaw, IMO, is that it can't (or chooses not to- I don't understand anything about JS-adjacent build systems and how they work) understand the difference between "code I write" and "code someone else wrote". Arguably no language in the world makes that distinction and Microsoft's "Just My Code" debugger tools are fantastic but use a lot of heuristics that you generally tell get as many false positives as false negatives, which is likely why it would always be a toggle. That said, I do think Typescript could make a better distinction about types that come in from code it has compiled itself (directly seen) and types that come by way of definition files with no code attached ("hearsay" types). (Again, I can't think of any language today that trusts code versus symbols files so differently, so this would be a new thing.) Often I want it for the: I'm looking at the JS or its documentation right now and it would let me call it this way, but for some reason the Community types are too strict and I don't want to file a DT PR and get into some philosophical discussion about authorial intent versus JS as written versus documented examples and which is "most true" so I'm just going to have to cast this API to any for now and call it a day. But I can also see the point in: don't give me strictness errors outside of the fence of code you are compiling for me right now in this project. And maybe more unsoundness tools, such as option to treat all return results as `unknown` and force defensive coding when using outside APIs. In general I think `unknown` isn't used enough in community types (it is still "too new"). Though `unknown` is also maybe too defensive for a lot of commmunity types, too. Also, if you want to code that defensively, you could always replace community types .d.ts files yourself with ones that use a lot more `unknown`.
- ragnese 5y ago> Arguably no language in the world makes that distinction That may or may not be precisely true, but there are languages that do things that are in that same ballpark. For example, Kotlin on the JVM "knows" when you're calling Java code and it handles certain things differently (null) than if you were calling Kotlin code- even if the code is from pre-compiled libraries. And considering that I already have a tsconfig file that describes my project and applies rules to only the code in my project (IIRC, there's a toggle to control whether the TS compiler analyzes/compiles your node_modules), I don't see why it would be such a leap to just have an event-stricter --strict flag that didn't allow known-unsound code in my project's files. > But I can also see the point in: don't give me strictness errors outside of the fence of code you are compiling for me right now in this project. And maybe more unsoundness tools, such as option to treat all return results as `unknown` and force defensive coding when using outside APIs. In general I think `unknown` isn't used enough in community types (it is still "too new"). > > Though `unknown` is also maybe too defensive for a lot of commmunity types, too. > > Also, if you want to code that defensively, you could always replace community types .d.ts files yourself with ones that use a lot more `unknown`. That's all a little intense, IMO. There's a clear difference between trusting a third party library's API/types and having the compiler help you catch bugs in your own code. If I call a third party function in my code and the documented types all check out. Then, if I experience a runtime type error, that's a bug in the library, not a bug in my code. This can happen in any language with escape hatches like type-casting and it's not appropriate, IMO, to guard against that. Put another way: if that is something that is a concern, then you shouldn't be using a third-party library.
- inbx0 5y agoBy sub-type variance are you referring to issues like this https://stackoverflow.com/questions/47850513/unsafe-implicit-conversion-of-generics-in-typescript https://stackoverflow.com/questions/47850513/unsafe-implicit... ? FWIW the lack of explicit variance annotations (and probably many other TS unsound design choices) are not so much related to supporting old untyped JS, but rather language design decisions that have to do with pragmatism and type system complexity. I can't find it now but I have a vague recollection Hejlsberg talking about the explcit variance annotations explicitly, how those were a huge can of worms in C# and they didn't want to bring them to TS. Unlike Flow, by the way, which does have explicit variance handling and doesn't suffer from the beforementioned foot gun (but does have its own foot guns of course).
- ragnese 5y agoYes and no. I do think that there are only two correct ways for a type system to do generic user-defined types: you either give us variance markers or you make all user-defined generic types invariant (Swift does this). Personally, I hate the latter, but not as much as I hate unsoundness. But, what I'm talking about is even more basic: https://www.typescriptlang.org/play?#code/MYGwhgzhAECCB2BLAtmEBvAvgKG6SMAIgPYDm0ApgB4AuF8AJjAimtOttF9AEZgBOAawAUASnaduU4MXgRiICgDoQZYQCIA7sWIAzdaMlccOPOCjQAwmBqVa9JnCSoQEqcgrFNYt1O4y5BWVVUg0PLwMjaBNcRHg6fl0wYApoAAlIAAUKWw4pAAccgC4nVhBsU10AV3hgGkRZaAgaKt1dYUL+eXgSjIhsmnE87k7upULbAF5oeApNKxsxCtwAehXoVDjY+IpE5NSAJQowBlkQAE8+knJhrn5j0-gL6AmS6+W8WWaNihKjk7Ol0g12g01uL2KMzm0GuS0w0Eg0ACzVW6wAygALYhVEAMaAASV4qTQqk0FDxNGILwICOg-0eFyuZGglNp1Vq9UaNAxNmgmjA8RgYHSWRyAH5sM1Wu0PIZsGtoAB1DHnFkY1LqkCuYACgDktmQqom0FO5D4QgR8HOyGI9zFTjxmhVJuIFBgiFsqgAbqkdTQkPkmlTkFVgBiJR5xjklOaROIFfwavUPJR+PxbQBCIA https://www.typescriptlang.org/play?#code/MYGwhgzhAECCB2BLAt... The code I wrote there is actually wrong for two reasons: 1. Type variance 2. Readonly doesn't do fuck-all in TypeScript for object types
- inbx0 5y agoYeah I agree, that's not what I'd consider good variance rules either. I guess my point was more about the reasons behind these weird rules. AFAIK interop with existing JavaScript was not the primary reason for doing them this way, if that's any consolation. FWIW your example wouldn't fly in Flow https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjGBDAzrsAQQDsBLAW2xgG8BfdLPAgETgHMwBTADwBdPiAEwIkKVMNVRhpYAEbYATgGsAFAEoJUmdoxxiuODE4A6eGxUAiBHDhQLardPr1MOfGADC2Xlz4DhRGSUMJra5JyI6qHaMrr6hiZmluGI9o5gzuikxPwKUNgYnGAAEngACpw+ktIADpUAXIFiMKguUACuxBi8pHpguLztUFAqdQoGxI2luBW8GtUyYxPGdT4AvGDEnEhevOqt6MDAYJTZWTmceQVFAEqc2IJ6MACe06wcC9IA1KuN7weYPQDE6cRp3B5PV54d5gDafMC-TbbMDvfYuI5gADKAAs4O0YIIwABJORFKjwBCcQm8OAIphgbBgcGPYgvN7sMA0hlgDpdHp9XjY7xgBDYHIERnTWYAflQAyGI3CDlQGIA6tjnpzsUVtTAQhgxQByHzkTWrMCPDjyZQM4jPchwBScaWBQkIDUWuCcAikHzwABuRQNvDINX6tPI7Qw2Nl4RWlWM1tUGgxCk6PXCXAUCkdAEIgA https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjGBDAzrsAQQDsBL...