5 ms·
It has holes, mainly because it has to be able to cover existing JavaScript codebases, which for many reasons can be an order of magnitude harder to type than c
by brundolf 5y ago
It has holes, mainly because it has to be able to cover existing JavaScript codebases, which for many reasons can be an order of magnitude harder to type than code that's written from the beginning to be statically typed.
The biggest holes can be filled via compiler options, and most of the rest can be papered over with the right coding practices (use Maps instead of Records, lint against `any` and type-casting, etc).
I haven't encountered the situation you described, but you could probably solve it by making the function generic over TArray extends Animal[].
The rest of what you've said doesn't line up with my experience. TypeScript is an imperfect tool that requires some elbow-grease to fully benefit from. I would prefer a typed language that didn't have to work under its real-world constraints (but not Java, because of null-checking at the very least), but over the years it's caught more bugs for me than I could possibly hope to count.
- ragnese 5y agoMany of the worst holes are indeed because of JavaScript compat. But, not all of them. See some of my other comments in this thread. There's no earthly reason that a `type` should automatically implement Record<string, unknown> and an `interface` shouldn't. That's just dumb and annoying- not necessarily "wrong". However, it's definitely wrong to allow me to pass `const o = {}` into a function that wants `Record<string, string>`. How in the world does TypeScript (with the strict flag set!) think that an empty object is able to return a string value for any arbitrary string key? That's absurd. The issue I raised about arrays isn't just arrays, actually. It happens with object fields as well. See https://www.typescriptlang.org/play?#code/MYGwhgzhAECCB2BLAtmE0DeBfAsAKFEhgGEwAXaAUwA8zL4ATGBFNTfaT6ZSgewHcAFAEoAXNABuvRA3Z4uC6MF7wIvEJQB0IXgHNBAIh4CDwjl1x5LhKNAAieqrXpM4SVOgznOAIzAAnAGsRcSkZOUUuZVV1LR19Az8g029oS0t8RHg6fwAzMGBKaAAJSAAFSgoveS4AB0rxFg98DLwsnPzCksgHXQi6hvs9Fvx8XIBXeGAyRBVuMCRa8fA6CrJBXnFSiDXhfs5eTXqKAF5oeEp+aFJ1sytRghUICk3uiF7oM+qFY-ELq96IhGeHwqEWy3IlDWGzu+EOx00SWCewA9CilLxkLVEBp-NBEDB6nlKNMQABPaAACzAtVqZIAhEA https://www.typescriptlang.org/play?#code/MYGwhgzhAECCB2BLAt... And, the `readonly` keyword has nothing at all to do with JavaScript. Yet, they added this keyword that is completely and utterly useless. To the point that I actually introduced a bug in my own code because I put `readonly` and `Readonly<>` everywhere and expected it to actually help me. It did help me... most of the time. And then it didn't. Because it turns out that you can always take a Readonly value, assign it to a non-Readonly variable and then just mutate the hell out of it, and the compiler won't even bat an eye: https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgJIFt0FcxwEYA2EAYgPanIDeAsAFDIPJQRwAmpIBAnsjOQFzIQWdHmh0AvnTqhIsRCgCyOfETIUa9Rn1KDho8bSm06MLCARhgHZAAs4IVmvIAKHYIzZchEuQCUggBupMCsVHSMyAgcAM5gyKRgttDqgsrezhQAvLzkEYyJyVDqAHQ6yDkAtACMktK00SBxubpomCo+6hXhWgzuyLVG9Y0xpEQlBKQA5m7kZf509o6Zs6R+dCNjEBPTq-NrQA https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgA... That's just... so bad. I don't even know what else to say. Sure, it caught some of my mistakes, but if it won't even catch all of the class of errors that it's supposed to, then I have to basically be just as careful as if I didn't use the feature at all. So, it's not reducing my mental burden at all. If anything, it's just giving us/me a false sense of security and tricking me into thinking that I DON'T need to be as careful.
- exdsq 5y agoI think you're overthinking this. TypeScript definitely helps catch issues - there have been large migrations to TS that were well received by engineers and project managers alike. Yes there are issues in the type system but you can find them in any language. Haskell has very strong claims but then you come across 1/0 != 1`div`0
- ragnese 5y agoYour Haskell example doesn't actually violate its own type system, though. 1/0 is Fractional or Double or whatever, so it gives Infinity. 1`div`0 is Integer and Infinity is not an Integer, so it explodes, instead. Definitely confusing. Perhaps poorly designed. But it's not type-theoretically incorrect. I'm complaining about TypeScript being literally incorrect in its handling of types. Not "strange" or "weird" or "surprising", but incorrect.
- brundolf 5y ago> However, it's definitely wrong to allow me to pass `const o = {}` into a function that wants `Record<string, string>`. How in the world does TypeScript (with the strict flag set!) think that an empty object is able to return a string value for any arbitrary string key? That's absurd. It's not. `Record<string, string>` doesn't mean it has a mapping for every string key (that's not possible), it means it might or might not have a mapping for any given string key; it really is analogous to a Map, and it's perfectly valid to pass an empty map to an argument that takes a map of a particular type. The only thing wrong with it is that `MyRecord[someKey]` will by default have type `string`, not type `string|undefined`. This it presumably to help with JS code that commonly indexes objects by strings without checking that the keys return values. Luckily, there's a compiler option to make it return `|undefined` (and it works on arrays too, which have the same problem by default): https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAc... > Because it turns out that you can always take a Readonly value, assign it to a non-Readonly variable and then just mutate the hell out of it I am surprised and disappointed that this works without casting, but I think with casting it's reasonable. TypeScript has to give you trap-doors to work around its checks when they don't serve your purposes; this is part of the "being overlaid on an imperfect language" thing. The important thing is that those circumventions are explicit and stand out. Though I will say, you definitely can't pass a readonly object/array to a non-readonly object/array function argument, so I'm doubly surprised your example works. I may dig into it further later just out of curiosity.