4 ms·
> It's not. `Record<string, string>` doesn't mean it has a mapping for every string key (that's not possible) Yes, it absolutely does mean that. Just because y
by ragnese 5y ago
> It's not. `Record<string, string>` doesn't mean it has a mapping for every string key (that's not possible)
Yes, it absolutely does mean that. Just because you can't correctly implement an object that satisfies the type doesn't mean that the type means something else (`never` is also a type that you can't implement).
> The only thing wrong with it is that `MyRecord[someKey]` will by default have type `string`, not type `string|undefined`.
Um, yeah. That's what's wrong. And therefore Record<string, string> literally does mean a string value for every string key.
The problem here is that some of these "utility types" have a specific intention behind them, and you aren't "supposed to" use Record this way. You're "supposed to" give it more specific types, like Record<'a' | 'b', string>.
But the compiler should be smart enough to know that nothing can possibly satisfy Record<string, string>, so if you write the type, it should be impossible to assign to it.
> 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#noUncheckedIndexedAc https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAc...
That's not the solution here, because it will cause a Record<'a' | 'b', string> to return string|undefined when I do `o['a']`, which is also wrong.
The solution is for TypeScript to handle Record correctly.
> 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.
I have no problem with casting. Having hard type casts does not make the language imperfect or incorrect.
> 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.
Definitely?
https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgEoTgEwPYgDYCeAYsBHpsgN4CwAUMg8lBjvgcnAFzIgCuAtgCNodAL506oSLEQoSZCjXqMuPAcKhiJtTBAR44zZDF4gEYYLmPZsACmzd55AJTcAbtmCZtu-YZQIuADOYMaOpOR0vgZGgSAhTNgw3OhYuIRO3jp6MQHBoVDYhcloLOkEADyprBkRmAB82jA2tjDOdM12xe20nbaF3UA https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgA...
- brundolf 5y agoFor the Record thing, I couldn't find a clear answer one way or the other in the docs on the "intended meaning" of that type. But I will say that in practice it behaves almost exactly like the meaning I described. So you can either take that meaning and run with it and be mostly happy, or insist on a different meaning that the implementation contradicts at nearly every turn and be upset about it (or you can just not use Record). > Definitely? I suspect one of the compiler options is to blame, and I just don't have time to dig into it right now. But I promise you, I've (gladly) had to convert many functions to take `readonly` arrays for their arguments because otherwise I couldn't pass a readonly array to them.
- brundolf 5y agoUpdate: I dug into your example, and you're right about `readonly` on objects, which is really unfortunate (but I'm glad to learn about it!). Looks like it's on the community's radar at least, so hopefully they fix it some day: https://github.com/Microsoft/TypeScript/issues/13347 https://github.com/Microsoft/TypeScript/issues/13347 Arrays are treated correctly though, at least: https://www.typescriptlang.org/play?#code/C4TwDgpgBAShCGATA9gOwDYgGIEsLsSgF4oBvAWACgoaoAnBFDEKeALilQFcBbAIwh0qAXypVQkKLnyESFarXadeAoZVGUqAMy6oAxsBxooW5MgAUyDtIIBKMlA3bdBo6ijAIAZ2Dm6yLQ44JDRMG0R7eVoTMz8A2xExTWSJaGCmTABBOjpiekZQlm5+QQBtAF1xcGhs3JJi1QqknX1DY1NkACZLDlrIx2aXNvdPH27-QNgC5j6yKmiOuK0E9SA https://www.typescriptlang.org/play?#code/C4TwDgpgBAShCGATA9... And despite the above, I still don't think `readonly` is useless or harmful. I benefit from it and I'm glad it exists, even if it could be better.
- ragnese 5y agoSo, yes, arrays are treated correctly. But think about this: JavaScript has 6 or so fundamental types: undefined, null, number, string, boolean, symbol, object, and array. All but two of those are primitive and therefore already immutable/readonly. So the readonly/Readonly<> feature is specifically for two of JavaScript's types. It only works on one of the two. That's 50%. We have a feature that only works for 50% of the cases it's supposed to. It's unacceptable that TypeScript even allows us to write `readonly` on object types. It should be an array-only feature until it actually does what it says it does. Is it really unreasonable for me to expect them to not offer a feature that literally does nothing when I use it? > And despite the above, I still don't think `readonly` is useless or harmful. I benefit from it and I'm glad it exists, even if it could be better. I disagree and I think you're wrong. And here's why. You just told me a few comments above that TypeScript has caught mistakes for you when you passed a readonly array (I'm assuming an array, because that's the only way it works) to a function that took a non-readonly array parameter. That sounds like a glass-half-full situation. Great! TypeScript helped you... But. You just learned today, from some arrogant jerk on HackerNews, that readonly does NOT work on object types. So, if I assume that you've also used readonly on object types (because why wouldn't you? You thought it worked.), it means that there is a GOOD chance that you've made the very same mistakes with objects as you admitted to making with arrays. However, TypeScript did NOT catch those mistakes for you. Instead, you thought your code was correctly handling readonly and non-readonly objects. So, did TypeScript do you a favor here or not? I sincerely believe that TypeScript did you a DISSERVICE. It tricked you. You probably relaxed the part of your brain that worried about that kind of thing when you wrote naked JavaScript. You relaxed it because you thought that TypeScript was actually helpful. And it DID catch some things, so why would you stress about it? I don't sit here and stress over whether I'm passing a number to a number field, because I know that TypeScript will tell me if I pass something that isn't a number. But what if it just didn't sometimes? TypeScript would literally be better if it didn't allow readonly on object types. The code that you and I, both, wrote would likely be MORE correct if we knew that TypeScript wasn't going to help us. We'd both sit at our desks and say "Oh shit, I'm passing an object to this function. I better be very careful to see if the function is going to mess up if I mutate the object after passing it in." Instead, you and I got duped, and thought that readonly actually did stuff that it says it does. So we didn't pause and think carefully. Fuck that. I'm becoming more and more convinced that everyone in this thread has Stockholm Syndrome. Everyone has just decided that TypeScript is awesome and I could point out 1,000 issues with its type system, and people are just going to say "well, don't use that" or "just pretend this type really means something other than what the compiler literally tells us". TypeScript could murder your cat and I'm pretty sure I'd get 10 replies saying "Well, I don't have a cat, so TypeScript is great!" or "Just don't have a cat. Duh."