Flow: [feature request] Use regex for a string type

Created on 19 Aug 2017  ·  13Comments  ·  Source: facebook/flow

I have a EventType which should be a string with only capital letters and underscores:

type EventType = string & ('HELLO_WORLD' | 'NICE_TO_MEET_YOU' | ...)

I expect to add a regex to check the strings are containing only capital letters and underscores:

// The type `string` could be removed, because the regex indicate the type should be a string
type EventType = string & `/^[A-Z]+(?:_[A-Z]+)*$/` & ('HELLO_WORLD' | 'NICE_TO_MEET_YOU' | ...)

// This would raise a flowtype error, because `nice_to_meet_you` doesn't match the regex 
type EventType = `/^[A-Z]+(?:_[A-Z]+)*$/` & ('HELLO_WORLD' | 'nice_to_meet_you' | ...)

How can I achieve this?

feature request

Most helpful comment

I agree this would be a useful feature. I have some hex color strings in my app, and it would be nice if I could set a regex to validate that the strings must match the format: /^#[0-9a-fA-F]{6}$/. (At compile time.)

All 13 comments

I'm curious why you want to add the regex check that the string has to be uppercase and underscores?

As long as all of the specific options you provide (HELLO_WORLD, NICE_TO_MEET_YOU, etc.) are uppercase and use underscores, then obviously only values like that will type check as EventType.

Adding the regex will not work at the type level, because flow is a type system that operates on the basis of what it can learn from the text of the program, without actually evaluating the code.

If what you want to enforce is that when developers create new types they have to be uppercase and underscored, that seems like something to enforce with a linter rule or a comment and code review. A type system is not the right way to enforce that kind of rule.

One more thought: it's usually (though not always!) an anti-pattern to have event types just be strings like this, especially if specific events can carry specific payloads.

For example, if HELLO_WORLD events always have a greeter attribute that's a string, and NICE_TO_MEET_YOU events have a time that's a Time, it would be best to represent the exact details of that with types, such as:

type EventType = 
    { type: "HELLO_WORLD", greeter: string }
  | { type: "NICE_TO_MEET_YOU", date: Date };

@asolove How can I set the linter rule to check the type being uppercase and underscored?

Sorry, I honestly don't know. I tried googling for an eslint rule to do it, but didn't find anything. In the codebases I've worked in, we've just done code review to ensure this.

Unless there is something Flow can help with, I think we can close this issue.

@asolove I think this could be done by flowtype check. It is a feature request.

For example, I could make a variable only be valid for HTTP/HTTPS URL by regex with flowtype:

type HTTP_URL = regex`{{HTTP RegExp rule here}}`;

const resourceURL: HTTP_URL = 'http://...';

There are so many places needs to be check whether a string is a valid format in my project.

Regardless if it's a good idea or not, I think a big difficulty here is that flow isn't written in Javascript, but OCaml. So if we wanted regex checking in flow, then flow either needs to implement a full Javascript regex engine, or we need to use OCaml syntax (like in the .flowconfig). I think the latter is not an option, and the former is likely a huge undertaking.

OCaml's regex engine is hot garbage.

There really is no way Flow can tell during type-checking time whether strings at runtime will match a certain regex.

So since you need to have code at runtime to check values and see if they are valid, a good pattern is to write a function that takes in a string and returns an opaque type marking some property of the string. If you want a type for URLs that are definitely valid, you could do:

opaque type URL: string = string;

function parseUrl(url: string): ?URL {
  if(/* some code to test if it's a url */ true) {
    return url;
  } else {
    return null;
  }
}

Now code outside of this module can accept and return URL-typed objects and trust that they really are valid URLs, because only this one function can create objects of that type, and it checks that they are valid.

@asolove I need to check at compile/transpile time, not runtime.

type HTTP_URL = regex`{{HTTP RegExp rule here}}`; 
const resourceURL: HTTP_URL = 'http://...';  // check this before transpiling

I agree this would be a useful feature. I have some hex color strings in my app, and it would be nice if I could set a regex to validate that the strings must match the format: /^#[0-9a-fA-F]{6}$/. (At compile time.)

I would also like to see this! We have a number of ids that need to be correctly formatted (imagine something like "source/connection/", a flow type might help, especially if we can compose these flowtypes. This would be great both as a form of documentation and keep us from making mistakes. What needs to be done to make it happen / what are the blockers?

Hi @asolove, @vkurchatkin and @xareelee

There are ways to make flow more powerful... but
I believe that you want keep it predictable... that may be costly:
Regular Expressions Are Not Regular, they may eat the Universe without giving result,
however it can be decided if the regular expression needs backtracking or not and this is safe

Like constants are used in sets 'aaa' | 'bbb' | 'ccc', (subset without backtracking of) regular expressions can be used between:
See this expression:

"ONLY" ("ONLY" | "OTHER") $CharSet<"EHLNORTY"> $Match</^[_A-Z]{4,5}$/> string mixed * any

  • value 'ONLY' will match all this types in flow
  • value "OTHER" match all except first.
  • value "NORTY" will match $CharSet<"EHLNORTY">
  • value "ONLY_ONE_NORTY" will match regular string predicate $Match</^[_A-Z]{4,5}$/>all about this — type $Match, this does not exists yet :smiling_imp:
  • then next... values "one norty and the others", 123, {a: 1}, …

This is really not much different from what is flow actually doing with all the types, but may be much more challenging.
However, there is very similar (may be even implementation base), compare this legacy code with the new one: type RegExp$flags = $CharSet<"gimsuy">

OCaml's regex engine is hot garbage.

Ugh yes, having yarn link-ed a local package source into my node_modules, Flow is walking into it, and trying to type-check the test code, which isn't working because the root project has some global libdefs for test() that don't apply to these tests.

Trying to write an OCaml regex for "ignore everything in this folder except the contents of lib/ is proving frustrating without the ability to use (?!lib/) as a pattern. And you can't explicitly include lib/ and exclude the rest because Flow processes ignores after includes and they supercede.

The alternative is ignore the whole lib and lose it's typedefs, or monkey about "publishing" it to a folder and linking to that instead, which will probably get unlinked whenever I use yarn next.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

john-gold picture john-gold  ·  3Comments

cubika picture cubika  ·  3Comments

mjj2000 picture mjj2000  ·  3Comments

bennoleslie picture bennoleslie  ·  3Comments

mmollaverdi picture mmollaverdi  ·  3Comments