-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Index Signature Defined to String but Accept Number #23328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Please see "Indexable Types" in https://www.typescriptlang.org/docs/handbook/interfaces.html |
I understand the rationale that JavaScript uses string underneath but it seems that TypeScript, when explicitly set to number, should respect the type. Otherwise, why just not letter developer set it to a number? Maybe I am missing some context, can you link me to the discussion about why TypeScript is not better than JavaScript in that scenario? |
You can think of numbers as being a subtype of strings for the purposes of indexing. They have predictable and meaningful runtime semantics when used in this way. It's not clear what error this check would be meant to prevent - if an object is indexable by a string, then writing to it by an implicitly-converted number key is not really problematic. |
I was bewildered when I saw a piece of code having IDs (string) used in a situation of index signature with an explicit mention that it should only accept a number. For me, it shatters the idea that TypeScript's explicit typing is always respected. Let me elaborate. A number is a number in many typed languages, even in TypeScript a variable of type number is not a subtype of string... the index signature exception makes a number a subtype of string weaken the trust of TypeScript's typing. I suggest that we keep it easy and reliable that when something is explicitly mentioned to be a type that this one is enforced like everywhere else in TypeScript. If the index signature can handle both, it seems natural to use a union (string | number) instead of setting it to a number. If the developer really wants to have a number in an index signature that is defined as a string, he should parse the number to be a string -- like anywhere in TypeScript. Maybe I am missing a detail that makes this exception better than follows TypeScript's principles. I am all open to learn about it. In all cases, the single line in the documentation for such a change in mindset doesn't seem to be the greatest way to make something clear from a consumer point of view. To answer your last sentence, we could also say that there is nothing fundamentally problematic in JavaScript once you master all the quirks. However, TypeScript's leverage is that you can explicitly have a "contract" and expect the code developed to follow it. The index signature exception or quirk that a number can accept a string violates, in my opinion, what the developer wrote and wanted to be: a number. |
If the documentation explain why every decision was made the way it was, it would ten times as big and would take ten times as long to read. Index signatures predate union types, so So then the question is, should we have broken everyone using The reason we don't let you write code like Now you might say, that's incorrect logic, because this code is rejected: function fn(x: string) { const s = x + ""; }
fn(10); // Error But that's because we don't really know that you didn't do this: function fn(x: string) { x.toLowerCase() }
fn(10); // Type error here prevents crash But if there were a type that defined "safely implicitly converts to string": function fn(x: safe_to_implicit_convert_to_string) { const s = x + ""; }
fn(10); // close enough
fn({}); // probably still wrong then we could define which types have meaningful implicit string coercions (number, boolean, string, Date) and allow those calls while still disallowing others. If we had the "safely implicitly converts to string" type, that's the type that would be implied by writing a string index signature: type t = { [key: safe_to_implicit_convert_to_string]: T }; ...but we don't. However, you still do get that behavior with regard to |
Thank you, Ryan, for the explanation. Now, I understand the motive of not breaking previous codes, but I am still not entirely sold to the idea of explicitly a type and allowing more. Concerning the documentation, if a particular case (like this one) is not fully documented, then you may have people asking questions. This thread will act as future documentation for people who are wondering about this quirk. So, great. That being said, I enjoy seeing many decisions discussed in Github's threads which act as "advanced documentation". I initially thought that you might have a link to one about that subject. However, I am pleased with your response. Thanks that you took your time to do it today. If I may abuse your time a little bit more, I am still questioning why the union definition between the two accepted type (string and number) is illegal syntax in TypeScript:
Since a number is safe to be a string, and a string is also legal, why is the union of these two legal primitives is not? I understand that it may be cumbersome since writing a number handles both cases, but the TS compiler error message mentions that it must be a string OR a number and someone that would like to pass both will use the union if he doesn't know he can use a number for the case of number and string. (I am advocating here to keep the magic trick of number to preserve compatibility AND to support union). In the end, what I understand is that we do not really care about the type of the index signature, neither we care about the name of the index signature. If we think outside the box, we could have a syntax without the name and without a type, and have TypeScript handling any type passed (which would accommodate the option to give a boolean which is legal in JavaScript even if not pragmatic).
|
You have to think about this historically, not from a clean slate. There is path dependence in the world. The decisions made six years ago affect the decisions we make now; you can't just imagine what the perfect syntax/behavior should be today and then assume that any deviation from that implies a bug or thing that ought to exist.
Adding the |
This is a good question. The current syntax is not naturally logic when we compare to the rest of the language. We already discuss this, and we agree that because of a historical reason it ended in the present form. However, I see it more as a step into cleaning the language. TypeScript could in the future have a warning to deprecate the magic string/number safe conversion and in a farther future removes the warning into a compilation error. Evolution is normal beyond technology like in speaking languages (e.g ., the English language from 100 years ago is different from now). Past debts are typical, I am not disputing this. However, like any garden, we must groom it and not just plan new flowers. I am 100% behind not having radical changes that break people suddenly. Microsoft, in general, is stellar by having long-term support and having good developer experience by keeping thing functional as possible. Kudos. However, it is sane to have a gradual process to lean into a cleaner state.
Many concepts have different in syntax and do the same thing. On the top of my head:
The evolutionary suggestion is not confusing if the warning message specifies the reason. You can even link it to the content we are generating here that provide context. Any rational person will understand that in the past, some features were not there in TypeScript to support the ideal syntax. However, many years later, it is available and to keep TypeScript aligned to be easy and consistent in the language, few minor syntaxes evolve. Again, thank you very much for your thorough follow-up and to take into consideration my different perspective on the topic. |
Is this issue related? #22105 |
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed. |
TypeScript Version: 2.8.3-insiders.20180407
(Also in the Playground)
Search Terms: index signature
Code
Expected behavior:
The last line of the code, I would expect to have TypeScript to say the same error as the second line. The error should say that the index must be a string.
Actual behavior:
The code compiles without error even if it is mentioned that only string must be accepted.
Playground Link: https://www.typescriptlang.org/play/index.html#src=let%20x%3A%20string%20%3D%20%22x%22%3B%0Ax%20%3D%201%3B%20%2F%2F%20As%20expected%2C%20this%20line%20doesn't%20compile%0A%0Ainterface%20Obj%20%7B%20%0A%20%20%20%20%5Bid%3A%20string%5D%3A%20boolean%3B%0A%7D%0Alet%20y%3A%20Obj%20%3D%20%7B%7D%3B%0Ay%5B%22okay%22%5D%20%3D%20true%3B%0Ay%5B123%5D%20%3D%20false%3B%20%2F%2F%20The%20id%20is%20set%20to%20string%2C%20why%20does%20it%20compiles%3F
Related Issues: Couldn't find.
The text was updated successfully, but these errors were encountered: