Flow: Using constants as type field names

Created on 20 Jun 2018  Â·  8Comments  Â·  Source: facebook/flow

Can flow distinguish between field declaration using constant and indexer declaration?

const A = 'a';
const B = 'b';

type Foo = {
  [A]: string,
  [B]: string,
}

7: [A]: string,
^ Cannot use string as a type because string is a value. To get the type of a value use typeof.
8: [B]: string,
^ Multiple indexers are not supported.

Try

feature request

Most helpful comment

Again people, no one tried to define multiple indexer types, but "computed properties" if you wish. In this scenario I'd like flow to treat const value as a key (not as a type). I understand that this is not supported currently, but what I would expect is the following transformation:

const A = 'a';
const B = 'b';
const C = 'c';

type Foo = {                 type Foo = {
  [A]: number,                  a: number,
  [B]: string,         =>       b: string,
  [C]: boolean                  c: boolean
}                            }

This is how it works in typescript

All 8 comments

The { [a]: b } Notation is reserved for indexed properties. This defines a map from type a to type b. What you want to use this notation for is to externalize the keys of specific properties. Is far as I know this is not supported.

Even if it was supported though you would have another problem. A and B are values. They may be constant but as far as flow is concerned they are still values so you would have to use typeof to get their type. What you can do though is convert A and B to types where 'a' and 'b' literal types.

As long as the property types you want to associate with A and B are the same you can then create a union of them and use that as the key type

Try

You can do this right now.

If/once https://github.com/facebook/flow/pull/4175 gets merged, you wouldn't need to write the types of A and B explicitly, cause it wouldn't be just string anymore.

The multiple indexer's bit would still be unsupported though. I'll tag this as a feature request for that.

Thanks @chisui and @mrkev. I'm aware about the union workaround, but it won't work if fields have different value types. I'm not trying to define indexer/multiple indexers, but define fields using consts as their names (works in typescript and thought it would be nice if flow had something similar)

const A = 'a';
const B = 'b';
const C = 'c';

type Foo = {
  [A]: number,
  [B]: string,
  [C]: boolean
}

Yup, that's what I meant by the multiple indexer bit; flow supports only one. I agree it could come in handy.

You can have a union of the value types like you can have a union of index types. Unfortunately that would allow all values of any value types to be assigned to a field of any index type since there effectively exist only one index type and one value type.

Try


I just pondered how would multiple indexers work. What should happen if index types intersect?

type T = {
  [ A | B ]: V,
  [ B | C ]: W,
}

The type of T[B] would be V | W. That could get complex fast if the index types are non trivial or if there are more indexers.

The first one to match could be taken. That's kind of how the definition for Object works, with that last honeypot case: https://github.com/facebook/flow/blob/v0.75/lib/core.js#L69

There could also be static analysis in place to ensure they don't intersect, and throw an error (or a warning) if they do ¯\_(ツ)_/¯

Again people, no one tried to define multiple indexer types, but "computed properties" if you wish. In this scenario I'd like flow to treat const value as a key (not as a type). I understand that this is not supported currently, but what I would expect is the following transformation:

const A = 'a';
const B = 'b';
const C = 'c';

type Foo = {                 type Foo = {
  [A]: number,                  a: number,
  [B]: string,         =>       b: string,
  [C]: boolean                  c: boolean
}                            }

This is how it works in typescript

I'm hitting this because I expected it to work so I could define immutable records and only define the key once.

export const MY_KEY = 'my-key';
export type MyRecordProps = {
  [MY_KEY]: ?string
};
export const MyRecord: RecordFactory<MyRecordProps> = Record({
  [MY_KEY]: null
});

Then later...

record.get(MY_KEY);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

marcelbeumer picture marcelbeumer  Â·  3Comments

jamiebuilds picture jamiebuilds  Â·  3Comments

doberkofler picture doberkofler  Â·  3Comments

philikon picture philikon  Â·  3Comments

funtaps picture funtaps  Â·  3Comments