Skip to content

Problem with Literal Types extracted from an object using $Values<typeof OBJECT> #252

@ndbroadbent

Description

@ndbroadbent

This is a:

  • Bug Report
  • Feature Request
  • Question
  • Other

Which concerns:

  • flow-runtime
  • babel-plugin-flow-runtime
  • flow-runtime-validators
  • flow-runtime-mobx
  • flow-config-parser
  • The documentation website

Background

I have been using an "enum" pattern with Flow that makes it a little easier to work with types.

First, I define some constants where they have a Literal Type for a specific string. Then I use these constants to build an object with some accessor keys. Finally, I use $Values to grab the types of each value and build a string enum for the type. Here's a real example from my application:

export const FIELD_TYPE_STRING: 'string' = 'string'
export const FIELD_TYPE_NUMBER: 'number' = 'number'
export const FIELD_TYPE_BOOLEAN: 'boolean' = 'boolean'
// ...

export const FIELD_TYPES = {
  STRING: FIELD_TYPE_STRING,
  NUMBER: FIELD_TYPE_NUMBER,
  BOOLEAN: FIELD_TYPE_BOOLEAN,
  // ...
}
export type FieldType = $Values<typeof FIELD_TYPES>

Then I can write code like this:

const type: FieldType = FIELD_TYPES.STRING

I can confirm that this pattern works fine with vanilla Flow, and Flow will raise an error if I try to assign "foo" to a variable with the FieldType type.

However, it looks like flow-runtime is struggling with these literal types, and it turns everything into string | string | string | ...

I am trying to see if I can use flow-runtime to automatically extract some of these enums and use them in other parts of my application (in a different programming language.)

What is the current behaviour?

Code (for https://gajus.github.io/flow-runtime/#/try)

import t, { reify } from 'flow-runtime'
import type { Type } from 'flow-runtime'

export const THING_TYPE_ONE: 'one' = 'one'
export const THING_TYPE_TWO: 'two' = 'two'
export const THING_TYPE_THREE: 'three' = 'three'

export const THING_TYPES = {
  ONE: THING_TYPE_ONE,
  TWO: THING_TYPE_TWO,
  THREE: THING_TYPE_THREE,
}
type ThingType = $Values<typeof THING_TYPES>

type Thing = {
  id: string | number;
  name: string;
  thing: ThingType;
};

const widget: Thing = {
  id: 123,
  name: 'Widget',
  thing: 'unknown'
};
console.log(widget);
console.log(ThingType.unwrap().toString())

Output:

{
  id: 123,
  name: "Widget",
  thing: "unknown"
}
string | string | string

What is the expected behaviour?

I am expecting the widget declaration to fail with an error like this:

Thing.thing must be one of: "one" | "two" | "three"

Expected: "one" | "two" | "three"

Actual Value: "unknown"

Actual Type: string

I am also expecting ThingType.unwrap().toString() to return "one" | "two" | "three".

Workaround

In the meantime, I could change my code and manually define the string enum:

export const FIELD_TYPE_STRING: 'string' = 'string'
export const FIELD_TYPE_NUMBER: 'number' = 'number'
export const FIELD_TYPE_BOOLEAN: 'boolean' = 'boolean'
// ...

export const FIELD_TYPES = {
  STRING: FIELD_TYPE_STRING,
  NUMBER: FIELD_TYPE_NUMBER,
  BOOLEAN: FIELD_TYPE_BOOLEAN,
  // ...
}
export type FieldType = 'string' | 'number' | 'boolean' | ...

But I would love to avoid the duplication and be able to use $Values to extract these Literal Types.

Which package versions are you using?

Latest versions on https://gajus.github.io/flow-runtime/#/try

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions