Repository navigation
keyof any incorrectly maps (only) to type string #21983
Description
Activity
If
symbolof(#20721) was supported, you could write it like this:function foo<T>(bar: T, baz: keyof T | symbolof T) { console.log(bar[baz]); } const sym = Symbol(); const quirk = { [sym]: "thing" }; foo(quirk, sym);
(There are no
numberproperty keys. All non-symbol property keys are either strings or numeric strings.)Reacted by Aluan Haddad- addedDuplicateAn existing issue was already createdAn existing issue was already created
on Feb 16, 2018 I just think it's not appropriate as a widened type for the result of
keyof.As for making keyof return symbols.... Object.keys only returns string keys at runtime, and many other JS constructs only operate over string keys. I think we may do symbolof operator to maintain compatibility and keep a distinction between the two namespaces.
ogwh that was in Wesley Wigham (@weswigham)'s comment for why
keyofshould be just strings.As for
keyof any, since this is not referring to the keys of any particular object, why not just use thePropertyKeytype in your annotation instead?Reacted by Aluan HaddadI don't mind having
keyOfandsymbolOffor backward compatibility but can we also haveanyKeyOfwhich would return both type of keys because I can't think of a use case where I would want to usesymbolOfon its own.Having
keyofonly refer to string keys can be useful, introducingsymbolofseems to be the right way to go. My guess is most of the time people are going to needkeyofmore thansymbolofanyway. If someone consistently needs both they can just create a type aliastype SymbolKeyOf<T> = keyof T | symbolof T.Reacted by Aluan Haddad- Symbols are usually used to store hidden values on an object, and are used far less than string keys.
keyofas is can be useful when usingObject.keysorObject.entrieswhich do not return symbols as keys.
keyofhas already been declared as string keys, and is being used as such, why break it? It also makes sense seeing asObject.keysrefers only to strings.Can you provide a use case where you would need to use
keyof T | symbolof Tmany times throughout? Is there a instance where an alias wouldn't be enough?1 remaining item
ogwh that could be fixed either way: either by (a) adding symbols to
keyof, or by (b) addingsymbolofand redefiningReadonly<T>to usekeyof T | symbolof T.So yes, that is a problem with the current definitions, but its not necessary an argument for one approach over the other.
Reacted by Robbie Speedogwh Here's a simple example of how expanding
keyofto include symbols could break existing codeconst sym = Symbol(); function values <T, K extends keyof T>(obj: T) { return Object.keys(obj).map((key: K) => { return obj[key]; }); } const exampleObj = { a: 1, b: 2, [sym]: '', }; const numbers: number[] = values(exampleObj);Currently works perfectly, but if symbols were included you'd get the resulting type error
Type '(string | number)[]' is not assignable to type 'number[]'.When in reality that would not be the correct type to return.
In response to some of your summary:
- I think I've shown an example that proves this is not something based merely on wording. For a long time there were no symbols in JS, hence why when talking about objects, a key generally refers to a string property (even if technically the spec defines it as either string or symbol).
- (+ 7, 8) I'm afraid I don't get what your point is here. Though you are correct that
Object.keysdoes not directly correlate tokeyof, in most cases though usage ofkeyofas the return type ofObject.keysdoes work (and is useful).
- My example above shows your proposal would be a breaking change.
symbolofwould not break anything, and allow us to do everything we would need to
One last note about expanding
keyof
If one were to try and separate keys into 2 types of symbol and string:interface O { a; b; c; [sA]; [sB]; } type OStringKeys = keyof O & string; type OSymbolKeys = keyof O & symbol;it would result in this mess:
type OStringKeys = ("a" & string) | ("b" & string) | ("c" & string) | (unique symbol & string) | (unique symbol & string) type OSymbolKeys = ("a" & symbol) | ("b" & symbol) | ("c" & symbol) | (unique symbol & symbol) | (unique symbol & symbol)if we left
keyofalone and addedsymbolof, we'd get:type OStringKeys = keyof O = "a" | "b" | "c" type OSymbolKeys = symbolof O = unique symbol | unique symbologwh it is producing an error because you've enabled strict function types, and is due to the fact that
Object.keysdoes not return the type(keyof T)[], there was a proposal to have it do so, but that wouldn't be correct in all scenarios (discussion on that).
Here's a updated version with strict function types enabled (Also tested locally with 2.7.2)typescript-bot commented
on Mar 7, 2018 ContributorMore actionsAutomatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
- addedFixedA PR has been merged for this issueA PR has been merged for this issueand removedDuplicateAn existing issue was already createdAn existing issue was already created
on Apr 23, 2018 - locked and limited conversation to collaborators
on Jul 31, 2018
TypeScript Version: 2.7.1 and 2.8.0-dev.20180215
Search Terms: "keyof any"
Code
Expected behavior:
keyof anyshould be equivalent toPropertyKey(that isstring | number | symbol)The example code should compile without error.
Actual behavior:
keyof anyis equivalent tostringThe example code does not compile and produces error:
error TS2345: Argument of type 'unique symbol' is not assignable to parameter of type 'string'.Playground Link:
https://www.typescriptlang.org/play/#src=function%20foo%3CT%3E(bar%3A%20T%2C%20baz%3A%20keyof%20T)%20%7B%0D%0A%20%20%20%20%20%20%20%20console.log(bar%5Bbaz%5D)%3B%0D%0A%7D%0D%0A%0D%0Aconst%20sym%20%3D%20Symbol()%3B%0D%0Aconst%20quirk%20%3D%20%7B%20%5Bsym%5D%3A%20%22thing%22%20%7D%3B%0D%0A%0D%0Afoo%3Cany%3E(quirk%2C%20sym)%3B