6to5 supports computed properties:
var c = {[1+1]: 2};
But flow produces an error for this: "computed property keys not supported". Any way to get around that error?
This is actually an ES6 feature. I would consider this as a sub-bug of #62.
Yeah, this shouldn't be too hard to fix. Basically just need to decide how computed properties should affect the object type and implement it here. We can't statically know what the property key will be, so my guess is that we can use computed properties to infer the indexer types. So something like
var x: { [key: string]: number } = { ["hello"]: 123 };
Is that really a concern? Property keys are always coerced to strings, and the computed property syntax doesn't apply to the new ES6 Map/WeakMap collections where keys can be of diverse types.
FWIW I had a simple case that I couldn't get flow to ignore, something like:
var eventHandlers = {
[Actions.DELETE_ITEM]: deleteItem
}
I couldn't get flow to stop giving me an error on this, so I transformed it to:
var eventHandlers = {};
eventHandlers[Actions.DELETE_ITEM] = deleteItem;
Obviously I'm not getting any type checking on eventHandlers
but at least flow is checking Actions.DELETE_ITEM
and not giving me any errors.
Object maps with string keys are always far faster than Maps, anyways, even natively implemented ones. And there are only three types you should ever pass to a computed property name: string | symbol | number
. If typeof
doesn't return either of the first two, then ES2015 requires it to be coerced to a string. (Engines usually optimize array-like accesses, and bail on computed string indices)
// Basic syntax
let obj = { [foo]: bar };
// Equivalent semantics
type KeyType = string | symbol;
let _object = {};
_setProperty(_object, foo, bar);
obj = _object;
function _setProperty(obj: {[key: KeyType]: any}, key: any, value: any) {
if (typeof key !== 'string' && typeof key !== 'symbol') key = String(key);
obj[key] = value;
}
// Typical engine's implementation
type KeyType = string | number | symbol;
let _object = {};
%setPropertyInline(_object, foo, bar);
obj = _object;
function %setPropertyInline(obj: {[key: KeyType]: any}, key: any, value: any) {
if (!%isInteger(key) && typeof key !== 'symbol') {
%deoptArrayLike(obj); // pseudo-operation
if (typeof value !== 'string') {
key = %ToString(key);
}
}
obj[key] = value;
}
@gabelevi @samwgoldman - any progress on this one?
No progress, but I think we need more discussion about how to type objects with not-statically-knowable keys. I think Gabe's recommendation of inferring an indexer type is a good one, but there are some existing issues with indexer types, notably #187.
Furthermore, I think we could do a better job on this one if we tackled it after const
support lands, because we will be able to know infer more statically, and fall back to indexer types in fewer scenarios.
Minor thought - wouldn't a warning rather than an error make sense here?
It's, after all, not really a problem with the code so much as it is a statement "hey, we can't work this out atm. Here be dragons."
It will naturally go away when Flow supports them.
Hmm. Unlike a compiled language, you can still run JavaScript that fails to type check. In a way, all errors are warnings. You can even silence the errors/warnings using the supress_comment
configuration. See the test for that feature for an example of its use.
True, I just mean that semantically it's not an _error_ in the code that Flow is checking so... seems odd to claim it is. Flow hasn't found an error - it just has a problem due it's feature set.
Warnings vs Errors is a very common linter and compiler distinction so it would make a certain amount of sense.
But yes, there's obviously a workaround with the supress_comment config
Could { ["foo"]: bar }
, as a special case, be treated equivalently to { foo: bar }
?
I am working on a small part of this, which covers literal strings and const-declared variables. Possibly I will include some inference to detect de-facto const bindings (#482), but I haven't dug into it.
:+1: for using with redux:
case REQUEST_POSTS:
return Object.assign({}, state, {
[action.reddit]: posts(state[action.reddit], action)
});
Bump
Any update on this ? It's been year since it was opened
@thejameskyle ;)
@brentvatne I really need this... https://github.com/sindresorhus/refined-github/issues/273#issuecomment-236408600
Brought up possibly doing something like this with @samwgoldman in the future to be a little bit more strict, but that would add yet another concept to object types. So just noting this for history.
declare function bar(): string;
var foo = { [bar()]: true };
if (foo.baz) { let baz: boolean = foo.baz }
// valid
if (foo.baz) { let baz: string = foo.baz }
// ^^^^^^ string incompatible with boolean (true)
let baz = foo.baz;
// ^^^ property `baz`. Property not found in
// multiple computed props:
{ [bar()]: true, [baz()]: 3 }; // { [key: string]: boolean | number }
I'm going to close this issue because they are supported to a sensible degree right now.
@thejameskyle, what about this:
/* @flow */
class Foo {
[Symbol.iterator](){}
}
4: [Symbol.iterator](){}
^ computed property keys not supported
Maybe a separate issue is best for that #2286 since everyone in here is discussing objects.
Also, symbols are a separate issue entirely as they aren't supported anywhere yet.
@thejameskyle
I'm trying to typecheck a simple computed property but flow seems to be missing an error.
I would expect this code - Try Flow Link - to catch the type error with returning a string instead of a number. Is there any way to get flow to catch this when using computed properties?
type Terms = {
[key:string]: number
}
function terms(name: string):Terms {
return {
[name]: 'Not a number'
}
}
Thanks.
Does the current level of support for computed property names work for class methods (prototype methods)?
e.g. Using flow-bin version 0.37.4 will error out with "computed property keys not supported" for:
const METHOD_NAMES: { [id:string]: string } = {
findItem: 'findItem'
};
class X {
[METHOD_NAMES.findItem]() {
console.log(`in ${METHOD_NAMES.findItem}`);
}
}
Run into a similar issue @mkellyclare did, and also while trying to narrow the problem down, discovered there seems to be something weird going on with type aliases as well:
type MyKeyType = string;
type MyValueType = number;
type MyMap = { [MyKeyType]: MyValueType };
let dynamicKey1 = '123';
let myMap1: MyMap = {
[dynamicKey1]: 'bazinga'
};
let dynamicKey2: MyKeyType = '123';
let myMap2: MyMap = {
[dynamicKey2]: 'bazinga'
};
myMap1
throws an error, but myMap2
doesn't.
@mkellyclare Did you ever find a work-around for this? This issue seems to persist in 0.53.0.
@adamjernst
No, haven't been able to work around this, I opened a separate issue for the specific case here but that's still open too. Was trying to type elastic search query objects, but this issue makes it impossible.
Finally ended up here after looking through a variety of issues (https://github.com/facebook/flow/issues/252 https://github.com/facebook/flow/issues/560 https://github.com/facebook/flow/issues/3258 https://github.com/facebook/flow/issues/4310).
It appears the current state of things is to throw in a // $FlowFixMe
. I have also been unable to locate any timeline or effort to resolve this. It's important to note I'm not bunching in Symbol support that is described in some of these places. I would like this rather common pattern to be supported, please.
Abstract ES6+ Example:
const buildObjectWithKey = (key, value) => ({
[key]: value
});
const newObject = buildObjectWithKey('foo', false);
React-based Example:
type Props = {
label: string,
isActive?: boolean,
activeClassName?: string,
};
const NavigationItem = ({ label, isActive, activeClassName }: Props) => (
<button
className={classNames({ [activeClassName]: activeClassName && isActive })}
>
{label}
</button>
);
Is this something being actively worked on? If not, could someone provide a bit of guidance where to start working on this problem? It appears to have been hanging around for quite some time and I'd be happy to take a look at resolving this issue given a good place to start looking.
Thank you!!
Most helpful comment
@thejameskyle, what about this: