Marcus Junius Brutus Marcus Junius Brutus - 1 year ago 165
React JSX Question

Flow requiring annotation on object literal

Assuming that

is defined as:

var cx = require('classnames');

… I have the following JSX code:

test: function(panelEnabled: boolean) {
const klass = cx({disabled: !panelEnabled});
return (<div className={klass}>foo</div>);

The above results in Flow 0.35.0 complaining with the following message:

20: const klass = cx({disabled: !panelEnabled});
^^^^^^^^^^^^^^^^^^^^^^^^^ object literal. Could not decide which case to select
17: ...classes: Array<$npm$classnames$Classes>
^^^^^^^^^^^^^^^^^^^^^^^ union type. See lib: flow-typed/npm/classnames_v2.x.x.js:17
Case 2 may work:
8: {[className: string]: boolean } |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ object type. See lib: flow-typed/npm/classnames_v2.x.x.js:8
But if it doesn't, case 3 looks promising too:
9: {[className: string]: ?boolean } |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ object type. See lib: flow-typed/npm/classnames_v2.x.x.js:9
Please provide additional annotation(s) to determine whether case 2 works (or consider merging it with case 3):
20: const klass = cx({disabled: !panelEnabled});
^^^^^^^^^^^^^ not operator

I can work around this issue with either of the below two approaches:

ugly but doesn't change the API of the method

test: function(panelEnabled: boolean) {
const klassSpec: {disabled: boolean} ={disabled: !panelEnabled};
const klass = cx(klassSpec);
return (<div className={klass}>foo</div>);

shorter but requires changing the API

test: function(panelDisabled: boolean) {
const klass = cx({disabled: panelDisabled});
return (<div className={klass}>foo</div>);

I have gotten the libdefs from flow-typed and the file I am pointed to by the flow message (
) is a mere 17 lines:

// flow-typed signature: 2dfd96b054f56a84f2d08769019d32d7
// flow-typed version: dc0ded3d57/classnames_v2.x.x/flow_>=v0.23.x_<=v0.27.x

type $npm$classnames$Classes =
string |
// We need both of these because if we just have the latter it won't accept objects typed
// explicitly as the former, due to mutation concerns.
{[className: string]: boolean } |
{[className: string]: ?boolean } |
Array<string> |
false |
void |

declare module 'classnames' {
declare function exports(
...classes: Array<$npm$classnames$Classes>
): string;

My questions are:

  1. what is the cause and significance of this message ?

  2. what is the proper way to handle this?

  3. why isn't it clear to Flow that if
    is also boolean.

Answer Source

It seems that you have installed the wrong version of the classnames definition file.

If you see the commented line in the definition file

// flow-typed version: dc0ded3d57/classnames_v2.x.x/flow_>=v0.23.x_<=v0.27.x

it indicates that this definition file is compatible with flow >= 0.23 up to flow 0.27.

Maybe you're using an out-dated version of flow-typed? Try updating it with npm install -g flow-typed.

I bootstrapped a project from scratch to test your code and it works correctly. Here's my package.json:

  "name": "flow-classnames",
  "scripts": { "flow": "flow" },
  "dependencies": {
    "classnames": "^2.2.5"
  "devDependencies": {
    "flow-bin": "^0.35.0"

and here's my index.js

// @flow

const cx = require('classnames');

const test = function(panelEnabled: boolean) {
  const klass = cx({disabled: !panelEnabled});
  return (<div className={klass}>foo</div>);

Steps to test:

npm install
flow-typed install
npm run flow

No errors!

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download