Repository navigation
Suggestion: Regex-validated string type #6579
Description
Activity
DanielRosenwasser commented
on Jan 24, 2016 MemberMore actionsYeah, I've seen this combing through DefinitelyTyped, . Even we could use something like this with
ScriptElementKindin the services layer, where we'd ideally be able to describe these as a comma-separated list of specific strings.The main problems are:
- It's not clear how to compose these well. If I want a comma-separated list of
"cat","dog", and"fish", then I need to write something like/dog|cat|fish(,(dog|cat|fish))*/.- If I already have types describing string literal types for
"cat","dog", and"fish", how do I integrate them into this regex? - Clearly there's repetition here, which is undesirable. Perhaps fixing the previous issue would make this easier.
- If I already have types describing string literal types for
- Non-standard extensions make this sort of iffy.
Reacted by douglance, ulrichb, Kazuki Yasufuku, Alex, tamsamani mohamed, Gary Kaganas, Jeroen Heijmans and Ma'rufReacted by Basarat Ali Syed, Jonas Schürmann, Frederic Barthelemy, Uladzimir Aleshka, Daniil Lipatkin, Ariel Santos, Nery Abinadi, Amit Beckenstein, zernie, the-unbearable-lightness-of-being and 15 moreReacted by Anselm Schüler and Ma'ruf- It's not clear how to compose these well. If I want a comma-separated list of
- addedSuggestionAn idea for TypeScriptAn idea for TypeScriptNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Jan 24, 2016 - addedDomain: Literal TypesUnit types including string literal types, numeric literal types, Boolean literals, null, undefinedUnit types including string literal types, numeric literal types, Boolean literals, null, undefined
on Apr 13, 2016 Huge +1 on this, ZipCode, SSN, ONet, many other use cases for this.
Reacted by Dawson Botsford, skbergam, Justin Christensen, Aaron Greenwald, Rojay Simpson, Haroen Viaene, JmQu, Shad Sterling, Debonil Ghosh, IG and 109 moreI faced the same problem, and I see that it is not implemented yet, maybe this workaround will be helpful:
http://stackoverflow.com/questions/37144672/guid-uuid-type-in-typescriptReacted by George Yong, Steven, Omid K. Rad, Johannes Millan, the-unbearable-lightness-of-being, Lina Blume and William CHAZOTReacted by Adam Žurek, Roy Prins and Jack SullivanAs Mohamed Hegazy (@mhegazy) suggested I will put my sugggestion (#8665) here. What about allow simple validation functions in type declarations? Something like that:
type Integer(n:number) => String(n).macth(/^[0-9]+$/) let x:Integer = 3 //OK let y:Integer = 3.6 //wrong type ColorLevel(n:number) => n>0 && n<= 255 type RGB = {red:ColorLevel, green:ColorLevel, blue:ColorLevel}; let redColor:RGB = {red:255, green:0, blue:0} //OK let wrongColor:RGB = {red:255, green:900, blue:0} //wrong type Hex(n:string) => n.match(/^([0-9]|[A-F])+$/) let hexValue:Hex = "F6A5" //OK let wrongHexValue:Hex = "F6AZ5" //wrong
The value that the type can accept would be determined by the function parameter type and by the function evaluation itself. That would solve #7982 also.
Reacted by Rajab Shakirov, Zack Chapple , Patrick Lienau, Alex Leung, Andrew Harper, AviFix, Tingan Ho, Daniel Opden Dries, Kevin Donovan, Vlad Sabev and 279 moreReacted by vinnichase, Moe Elsharif, Enteleform, Mikolaj Torz, the-unbearable-lightness-of-being, Tim Kinnane, @milmaj, Artur Grzesiak, Isaac Skelton, Andrew Faulkner and 23 moreReacted by vinnichase, Felipe Marinho, Enteleform, Ishmum Jawad Khan, Tim Kinnane, @milmaj, Artur Grzesiak, Dave Houlbrooke, Alexey Iskhakov, Ramon Balthazar and 38 moreReacted by Naimur Rahman, Rodrigo Bastos, Janek Eilts UG (haftungsbeschränkt) and Daniil KhlybovReacted by Ricardo Fernández Serrata, Janek Eilts UG (haftungsbeschränkt) and Daniil KhlybovRaphael Ferreira (@rylphs) +1 this would make TypeScript extremely powerful
Reacted by Shinigami, Felipe Marinho, Ralph Theart, Uladzimir Aleshka, Robbie, Jacob Massengill, Yoosif Sherif, Zac Milano, Motasem, Rodrigo Sanabria and 58 moreReacted by Charles Bayley and Daniil KhlybovHow does subtyping work with regex-validated string types?
let a: RegExType_1 let b: RegExType_2 a = b // Is this allowed? Is RegExType_2 subtype of RegExType_1? b = a // Is this allowed? Is RegExType_1 subtype of RegExType_2?
where
RegExType_1andRegExType_2are regex-validated string types.Edit: It looks like this problem is solvable in polynomial time (see The Inclusion Problem for Regular Expressions).
Reacted by Charles Samborski, Mike Keesey, tsemerad, Denis, pqnet, AnyhowStep, Zach Albia, Drini Cami, Pedro Augusto de Paula Barbosa, Nate Levin and 10 moreReacted by Michael PalazovWould also help with TypeStyle : typestyle/typestyle#5 🌹
Reacted by James Wiens, Pubudu Dodangoda and Ghabriel NunesDanielRosenwasser commented
on Oct 17, 2016 MemberMore actionsIn JSX, Ryan Cavanaugh (@RyanCavanaugh) and I've seen people add
aria-(and potentiallydata-) attributes. Someone actually added a string index signature in DefinitelyTyped as a catch-all. A new index signature for this would have be helpful.interface IntrinsicElements { // .... [attributeName: /aria-\w+/]: number | string | boolean; }
Reacted by Kitson Kelly, Vlad Sabev, Andrew Fong, jwbay, AA, Dmitry Luzanov, Daniel Leib, Shayan Sayahi, Nurbol Alpysbayev, Tomasz Błachut and 33 moreReacted by Jason Gore, Amit Beckenstein, Anurag Hazra and mohamed-taher-bytroDesign Proposal
There are a lot of cases when developers need more specified value then just a string, but can't enumerate them as union of simple string literals e.g. css colors, emails, phone numbers, ZipCode, swagger extensions etc. Even json schema specification which commonly used for describing schema of JSON object has pattern and patternProperties that in terms of TS type system could be called
regex-validated string typeandregex-validated string type of index.Goals
Provide developers with type system that is one step closer to JSON Schema, that commonly used by them and also prevent them from forgetting about string validation checks when needed.
Syntactic overview
Implementation of this feature consists of 4 parts:
Regex validated type
type CssColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
Regex-validated variable type
let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
and the same, but more readable
let fontColor: CssColor;
Regex-validated variable type of index
interface UsersCollection { [email: /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i]: User; }
and the same, but more readable
interface UsersCollection { [email: Email]: User; }
Type guard for variable type
setFontColorFromString(color: string) { fontColor = color;// compile time error if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) { fontColor = color;// correct } }
and same
setFontColorFromString(color: string) { fontColor = color;// compile time error if (!(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color))) return; fontColor = color;// correct }
and using defined type for better readability
setFontColorFromString(color: string) { fontColor = color;// compile time error if (CssColor.test(color)) { fontColor = color;// correct } }
same as
setFontColorFromString(color: string) { fontColor = color;// compile time error if (!(CssColor.test(color))) return; fontColor = color;// correct }
Type gurard for index type
let collection: UsersCollection; getUserByEmail(email: string) { collection[email];// type is any if (/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email)) { collection[email];// type is User } }
same as
let collection: UsersCollection; getUserByEmail(email: string) { collection[email];// type is any if (!(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email))) return; collection[email];// type is User }
and using defined type for better readability
let collection: UsersCollection; getUserByEmail(email: string) { collection[email];// type is any if (Email.test(email)) { collection[email];// type is User } }
same as
let collection: UsersCollection; getUserByEmail(email: string) { collection[email];// type is any if (!(Email.test(email))) return; collection[email];// type is User }
Semantic overview
Assignments
let email: Email; let gmail: Gmail; email = 'test@example.com';// correct email = 'test@gmail.com';// correct gmail = 'test@example.com';// compile time error gmail = 'test@gmail.com';// correct gmail = email;// obviously compile time error email = gmail;// unfortunately compile time error too
Unfortunately we can't check is one regex is subtype of another without hard performance impact due to this article. So it should be restricted. But there are next workarounds:
// explicit cast gmail = <Gmail>email;// correct // type guard if (Gmail.test(email)) { gmail = email;// correct } // another regex subtype declaration type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i; gmail = email;// correct
Unfortunately assigning of
stringvariable toregex-validatedvariable should also be restricted, because there is no guaranty in compile time that it will match regex.let someEmail = 'test@example.com'; let someGmail = 'test@gmail.com'; email = someEmail;// compile time error gmail = someGmail;// compile time error
But we are able to use explicit cast or type guards as shown here. Second is recommended.
Luckily it's not a case for string literals, because while using them we ARE able to check that its value matches regex.let someEmail: 'test@example.com' = 'test@example.com'; let someGmail: 'test@gmail.com' = 'test@gmail.com'; email = someEmail;// correct gmail = someGmail;// correct
Type narrowing for indexes
For simple cases of
regex-validated typeof index see Type gurard for index type.
But there could be more complicated cases:type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
interface UsersCollection { [email: Email]: User; [gmail: Gmail]: GmailUser; } let collection: UsersCollection; let someEmail = 'test@example.com'; let someGmail = 'test@gmail.com'; collection['test@example.com'];// type is User collection['test@gmail.com'];// type is User & GmailUser collection[someEmail];// unfortunately type is any collection[someGmail];// unfortunately type is any // explicit cast is still an unsafe workaround collection[<Email> someEmail];// type is User collection[<Gmail> someGmail];// type is GmailUser collection[<Email & Gmail> someGmail];// type is User & GmailUser
Literals haven't such problem:
let collection: UsersCollection; let someEmail: 'test@example.com' = 'test@example.com'; let someGmail: 'test@gmail.com' = 'test@gmail.com'; collection[someEmail];// type is User collection[someGmail];// type is User & GmailUser
But for variables the best option is using type guards as in next more realistic examples:
getUserByEmail(email: string) { collection[email];// type is any if (Email.test(email)) { collection[email];// type is User if (Gmail.test(email)) { collection[email];// type is User & GmailUser } } if (Gmail.test(email)) { collection[email];// type is GmailUser } }
But if we'll use better definition for
Gmailtype it would have another type narrowing:type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
getUserByEmail(email: string) { collection[email];// type is any if (Email.test(email)) { collection[email];// type is User if (Gmail.test(email)) { collection[email];// type is User & GmailUser } } if (Gmail.test(email)) { collection[email];// type is User & GmailUser } }
Unions and intersections
Actually common types and
regex-validatedtypes are really different, so we need rules how correclty handle their unions and intersections.type Regex_1 = / ... /; type Regex_2 = / ... /; type NonRegex = { ... }; type test_1 = Regex_1 | Regex_2;// correct type test_2 = Regex_1 & Regex_2;// correct type test_3 = Regex_1 | NonRegex;// correct type test_4 = Regex_1 & NonRegex;// compile time error if (test_1.test(something)) { something;// type is test_1 // something matches Regex_1 OR Regex_2 } if (test_2.test(something)) { something;// type is test_2 // something matches Regex_1 AND Regex_2 } if (test_3.test(something)) { something;// type is Regex_1 } else { something;// type is NonRegex }
Generics
There are no special cases for generics, so
regex-validatedtype could be used with generics in same way as usual types.
For generics with constraints like below,regex-validatedtype behaves like string:class Something<T extends String> { ... } let something = new Something<Email>();// correct
Emit overview
Unlike usual types,
regex-validatedhave some impact on emit:type Regex_1 = / ... /; type Regex_2 = / ... /; type NonRegex = { ... }; type test_1 = Regex_1 | Regex_2; type test_2 = Regex_1 & Regex_2; type test_3 = Regex_1 | NonRegex; type test_4 = Regex_1 & NonRegex; if (test_1.test(something)) { /* ... */ } if (test_2.test(something)) { /* ... */ } if (test_3.test(something)) { /* ... */ } else { /* ... */ }
will compile to:
var Regex_1 = / ... /; var Regex_2 = / ... /; if (Regex_1.test(something) || Regex_2.test(something)) { /* ... */ } if (Regex_1.test(something) && Regex_2.test(something)) { /* ... */ } if (Regex_1.test(something)) { /* ... */ } else { /* ... */ }
Compatibility overview
This feature has no issues with compatibility, because there only case that could break it and it is related to that
regex-validatedtype has emit impact unlike usual type, so this is valid TS code:type someType = { ... }; var someType = { ... };
when code below is not:
type someRegex = / ... /; var someRegex = { ... };
But second already WAS invalid, but due to another reason (type declaration was wrong).
So now we have to restrict declaring of variable with name same to type, in case when this type isregex-validated.P.S.
Feel free to point on things that I probably have missed. If you like this proposal, I could try to create tests that covers it and add them as PR.
Reacted by Gregory Guidero, Vlad Sabev, Alex, Alex Leung, Alexander Bird, Lars Gleim, Matt Zygmunt, Raphael Ferreira, Kalle Ott, Anatoliy Gordienko and 343 moreReacted by Jan, Felicitas Pojtinger, Gaëtan Rizio, Charlee Li, Victor Sena Molero, vinnichase, Daniel Chen, Jacob Madsen, Ralph Theart, Robert Mengual and 26 moreReacted by Den Irkhin, yoshino akira and Daniil KhlybovReacted by OfirTheOne, Felicitas Pojtinger, vinnichase, Jacob Madsen, Eduardo Grajales Villanueva, Timothé Pearce, RanolP, Ralph Theart, Benedikt Roth, Dominik Markiewicz and 39 moreReacted by Apo, Yann Braga, Andrew Faulkner, Jamie Haywood, Charlie Walter, Nick Paterno, 장재영, James, Qwertiy, Leonardo Giroto and 10 more130 remaining items
I confirmed this comment from Patrick Lienau (@rozzzly) works with TS 4.1.0 nightly!
type TLD = 'com' | 'net' | 'org'; type Domain = `${string}.${TLD}`; type Url = `${'http'|'https'}://${Domain}`; const success: Url = 'https://example.com'; const fail: Url = 'example.com'; const domain: Domain = 'example.com';
Try it in the playground and see that
failhas a compile time error 🤩This would solve the data- or aria- problem that most of us face in UX libraries if it can be applied to indexes.
Reacted by Shivaji Varma Pusapati VenkataBasically this but obviously that doesn't work because TS only allows string | number. Since this is essentially a string can it be enabled?
https://www.typescriptlang.org/play?target=99&ts=4.1.0-dev.20201001#code/LAKALgngDgpgBAEQIZicgzgCzgXjgAwBIBvAcgBMUkBaUgXxPTACcBLAOwHM78BuUDmBjMAZkgDG8AJIAVGEzjFQcFXADalVBkwAFZgHsoALmRakWALpGmbLvxB1QocfvYL0AV3GT06I3Fl5MFxFCipqISZSI1JIsHpeIAchadlavi-casebook commented
on Oct 1, 2020 More actionsUpdate: after playing with this feature a bit, it will not cover many use cases. For example, it doesn't work for a hex color string.
type HexChar = '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'; type HexColor = `#${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}`; let color: HexColor = '#123456';
Today, that fails with "Expression produces a union type that is too complex to represent.(2590)"
There was some reference to this limitation in the release notes. It creates a list of all the possible valid combinations, in this case it would create a union with 16,777,216 (i.e., 16^6) members.
Reacted by AnyhowStep, canonbrother, Connor Dooley, Chao Y, Max Nanasy and Joshua Cade BarberReacted by Steven, xpdmk, Ziad and Joshua Cade BarberThis is a great idea... Igmat made some incredible posts back in 2016 that look good on paper anyway.
I found this because I wanted to make sure the keys of an object literal passed into my function were valid css class names. I can easily check at runtime... but to me it seems so obvious that typescript should be able to do this at compile time, especially in situations where I am just hard-coding object literals and typescript shouldn't have to figure out if MyUnionExtendedExotictype satisfies SomeArbitraryRegexType.
Maybe one day I will be knowledgeable enough to make a more productive contribution :/
I confirmed this comment from Patrick Lienau (@rozzzly) works with TS 4.1.0 nightly!
Wow. I honestly did not expect to see this get implemented, not anytime soon at least.
Chad Lavimoniere (@chadlavi-casebook)
There was some reference to this limitation in the release notes. It creates a list of all the possible valid combinations, in this case it would create a union with 16,777,216 (i.e., 16^6) members.
I'd be curious to see how large that union could get before it became a problem performance wise. Steven (@styfle)'s example shows how easy it is to hit that ceiling. There's obviously going to be a some degree of diminishing returns of usefulness of complex types vs performance.
I wanted to make sure the keys of an object literal passed into my function were valid css class names
I'm fairly confident in saying that it's not possible with the current implementation. If there was support for quantifiers and ranges you would probably get validation for BEM style class names. The standard js regex for that isn't too terrible:
^\.[a-z]([a-z0-9-]+)?(__([a-z0-9]+-?)+)?(--([a-z0-9]+-?)+){0,2}$
You would also ditch the anchors because as the implementation stands, it's either an end-to-end match or nothing so^and$are implied. Now that's a comparatively simple regex for a narrow subset of what is a valid css selector. For example:ಠ_ಠis a valid class name. I'm not kidding. CSS selectors are very permissive swhich makes them extremely difficult to validate. So your desire is probably out of scope for template literal types, at least for the foreseeable future. 😞I'm sorry. I had to do this.
I implemented regular languages in TypeScript.
-
Playground #1, number of 1s is divisible by 3 but not by 2
-
Playground #2, hex string of length 6
More accurately, I implemented a simple deterministic finite automaton using TS 4.1
I mean, we can already implement Turing machines in TS. So, DFAs and PDAs are "easy", compared to that.
And template strings make this more usable.
The core types are actually simple and fit in < 30 LOC,
type Head<StrT extends string> = StrT extends `${infer HeadT}${string}` ? HeadT : never; type Tail<StrT extends string> = StrT extends `${string}${infer TailT}` ? TailT : never; interface Dfa { startState : string, acceptStates : string, transitions : Record<string, Record<string, string>>, } type AcceptsImpl< DfaT extends Dfa, StateT extends string, InputT extends string > = InputT extends "" ? (StateT extends DfaT["acceptStates"] ? true : false) : AcceptsImpl< DfaT, DfaT["transitions"][StateT][Head<InputT>], Tail<InputT> >; type Accepts<DfaT extends Dfa, InputT extends string> = AcceptsImpl<DfaT, DfaT["startState"], InputT>;
It's specifying the automatons that's the hard part.
But I'm pretty sure someone can make a regex to TypeScript DFA™ generator...
I'd also like to highlight that the "hex string of length 6" example shows you can make function parameters only accept strings matching the regex using ugly hackery,
declare function takesOnlyHex<StrT extends string> ( hexString : Accepts<HexStringLen6, StrT> extends true ? StrT : {__err : `${StrT} is not a hex-string of length 6`} ) : void; //OK takesOnlyHex("DEADBE") //Error: Argument of type 'string' is not assignable to parameter of type '{ __err: "DEADBEEF is not a hex-string of length 6"; }'. takesOnlyHex("DEADBEEF") //OK takesOnlyHex("01A34B") //Error: Argument of type 'string' is not assignable to parameter of type '{ __err: "01AZ4B is not a hex-string of length 6"; }'. takesOnlyHex("01AZ4B")
Here's a bonus Playground; it implements the regex
/^hello .*/And another Playground; it implements the regex
/ world$/One final example, Playground; this is a floating point string regex!
Reacted by Patrick Lienau, Anton Mikhailov, Scott Bedard, Anurag Hazra, csorfab, Michael Chinigo, Michele Nuzzi, Jinseok Seo (Jason Jin), Daniil Pankov, Max Nanasy and 4 moreReacted by Anurag Hazra, csorfab, Fabio Spampinato, Ivancing, Michele Nuzzi, Jinseok Seo (Jason Jin), Dhananjay Talekar, Laura Ann, Steven Kalt, BalaM314 and 3 moreReacted by Salvatore, iTob191, Patrick Lienau, Chayim Refael Friedman, Anurag Hazra, Michele Nuzzi, Jinseok Seo (Jason Jin), Ray, Laura Ann and Daniil KhlybovReacted by Daniel Lamando, btoo, Jamie Birch, Ashlynne Mitchell, Roman, Leon Yu, Shinigami, Jacques Rimbault, Florian ERNST, Salvatore and 12 more-
AnyhowStep Well i used your DFA idea to implement a simple regex
[abc]{4}which means the letters abc in any order with missing but exactly the length of 4. (aaaa, abcc, bbcc, etc...).
PlaygroundReacted by AnyhowStepReacted by Anton MikhailovReacted by Jacques Rimbaulthttps://cyberzhg.github.io/toolbox/min_dfa?regex=ZCgoYmQqYiopKmMpKg==
https://github.com/CyberZHG/toolbox
If I had more willpower, I'd grab something like the above and use it to turn regexes into TS DFAs™ lol
Okay, I just threw together a prototype,
https://glitch.com/~sassy-valiant-heath
[Edit] https://glitch.com/~efficacious-valley-repair <-- This produces way better output for more complicated regexes
[Edit] It seems like Glitch will archive free projects that are inactive for too long. So, here's a git repo with the files,
https://github.com/AnyhowStep/efficacious-valley-repair/tree/main/appStep 1, key in your regex here,

Step 3, click the generated TS playground URL,

Step 4, scroll down till
InLanguage_0,

Step 5, play with input values,

Shoutout to Kevin P. Dyer (@kpdyer) , author of https://www.npmjs.com/package/regex2dfa , for doing the heavy lifting of the conversion
Reacted by Ilya Borisov, Harpush, James Dunnam, Andrew M, pierre, Kristóf Poduszló, btoo, Niklas Mollenhauer, Marcus Riemer, iTob191 and 18 moreReacted by Ilya Medvedev and Daniil KhlybovIn case someone needs something a little more powerful, here's a Turing machine 😆
Reacted by Bogdan Butnaru, Jendrik, Chayim Refael Friedman, Gersom van Ginkel, fix-me and Michele NuzziRyanCavanaugh commented
on Oct 19, 2020 MemberMore actionsThis thread has gotten too long to read and many of the comments are either addressed by template literal types or are off-topic. I've created a new issue #41160 for discussion of what remaining use cases might be enabled by this feature. Feel free to continue discussing type system parsers here 😀
Reacted by Anton Bessonov, Jordi Oliveras Rovira, Nick McCrea, iTob191, Claudia Meadows, Norman Fuchs, Chayim Refael Friedman, Joe Calzaretta, fxtressia, Michał Czapliński and 11 moreReacted by Jan Potoms, Mariusz Pawelski, AnyhowStep, Monique Altero, Hayder and Hieu Pham (Web SRE / Platform Pillar)Here is a workaround :)
interface $A_MAP { a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; } type $a = keyof $A_MAP; type $aa = "a"; type $ab = $aa | "b"; type $ac = $ab | "c"; type $ad = $ac | "d"; type $ae = $ad | "e"; type $af = $ae | "f"; interface $A_UMAP { a: $aa; b: $ab; c: $ac; d: $ad; e: $ae; f: $af; } interface $D_MAP { 0: "0"; 1: "1"; 2: "2"; 3: "3"; 4: "4"; 5: "5"; 6: "6"; 7: "7"; 8: "8"; 9: "9"; } type $d = $D_MAP[keyof $D_MAP]; type $d0 = "0"; type $d1 = "0" | "1"; type $d2 = $d1 | "2"; type $d3 = $d2 | "3"; type $d4 = $d3 | "4"; type $d5 = $d4 | "5"; type $d6 = $d5 | "6"; type $d7 = $d6 | "7"; type $d8 = $d7 | "8"; type $d9 = $d8 | "9"; interface $D_UMAP { 0: $d0; 1: $d1; 2: $d2; 3: $d3; 4: $d4; 5: $d5; 6: $d6; 7: $d7; 8: $d8; 9: $d9; } type $Max_1<T extends string> = "" | `${T}`; type $Max_2<T extends string> = $Max_1<T> | `${$Max_1<T>}${T}`; type $Max_3<T extends string> = $Max_2<T> | `${$Max_2<T>}${T}`; type $Max_4<T extends string> = $Max_3<T> | `${$Max_3<T>}${T}`; type $Max_5<T extends string> = $Max_4<T> | `${$Max_4<T>}${T}`; type $Max_6<T extends string> = $Max_5<T> | `${$Max_5<T>}${T}`; type $Max_7<T extends string> = $Max_6<T> | `${$Max_6<T>}${T}`; type $Max_8<T extends string> = $Max_7<T> | `${$Max_7<T>}${T}`; type $Max_9<T extends string> = $Max_8<T> | `${$Max_8<T>}${T}`; interface $Max_Map<T extends string> { 1: $Max_1<T>; 2: $Max_2<T>; 3: $Max_3<T>; 4: $Max_4<T>; 5: $Max_5<T>; 6: $Max_6<T>; 7: $Max_7<T>; 8: $Max_8<T>; 9: $Max_9<T>; } type $Repeat_1<T extends string> = `${T}`; type $Repeat_2<T extends string> = `${$Repeat_1<T>}${T}`; type $Repeat_3<T extends string> = `${$Repeat_2<T>}${T}`; type $Repeat_4<T extends string> = `${$Repeat_3<T>}${T}`; type $Repeat_5<T extends string> = `${$Repeat_4<T>}${T}`; type $Repeat_6<T extends string> = `${$Repeat_5<T>}${T}`; type $Repeat_7<T extends string> = `${$Repeat_6<T>}${T}`; type $Repeat_8<T extends string> = `${$Repeat_7<T>}${T}`; type $Repeat_9<T extends string> = `${$Repeat_8<T>}${T}`; interface $Repeat_Map<T extends string> { 1: $Repeat_1<T>; 2: $Repeat_2<T>; 3: $Repeat_3<T>; 4: $Repeat_4<T>; 5: $Repeat_5<T>; 6: $Repeat_6<T>; 7: $Repeat_7<T>; 8: $Repeat_8<T>; 9: $Repeat_9<T>; } // regexp: /[a-f]/ type $arg<From extends $a, To extends $a> = | Exclude<$A_UMAP[To], $A_UMAP[From]> | $A_MAP[From]; // regexp: /[0-9]/ type $drg<From extends keyof $D_UMAP, To extends keyof $D_UMAP> = | Exclude<$D_UMAP[To], $D_UMAP[From]> | $D_MAP[From]; // regexp: /T{From,To}/ type $rp< T extends string, From extends keyof $Max_Map<T>, To extends keyof $Max_Map<T> > = $Repeat_Map<T>[From] | Exclude<$Max_Map<T>[To], $Max_Map<T>[From]>; // examples: // regexp: /[5-9]/ const reg0: $drg<5, 9> = "7"; // regexp: /[b-e]/ const reg: $arg<"b", "e"> = "d"; // regexp: /a{2,6}/ const reg1: $rp<"a", 2, 6> = "aa"; // regexp: /\d{1,3}/ const reg2: $rp<$d, 1, 3> = "22"; // regexp: /[3-5]{1,3}/ const reg4: $rp<$drg<3, 5>, 1, 3> = "334";
Reacted by RegExpRegExp and klm127Reacted by Daniel Lamando, Claudia Meadows, Thundercraft5, Amr, Dawid Wójcik, Arthur Ginzburg, Reza, Zxilly, Gabriel Vergnaud, Mario Mui and 4 moreReacted by Sander Mol, Ilya Borisov, Shivam Singla, Dawid Wójcik, Gaute Løken, Steven Nguyen and klm127Reacted by Ricardo Fernández SerrataHere is a workaround :)
interface $A_MAP { a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; } type $a = keyof $A_MAP; type $aa = "a"; type $ab = $aa | "b"; type $ac = $ab | "c"; type $ad = $ac | "d"; type $ae = $ad | "e"; type $af = $ae | "f"; interface $A_UMAP { a: $aa; b: $ab; c: $ac; d: $ad; e: $ae; f: $af; } interface $D_MAP { 0: "0"; 1: "1"; 2: "2"; 3: "3"; 4: "4"; 5: "5"; 6: "6"; 7: "7"; 8: "8"; 9: "9"; } type $d = $D_MAP[keyof $D_MAP]; type $d0 = "0"; type $d1 = "0" | "1"; type $d2 = $d1 | "2"; type $d3 = $d2 | "3"; type $d4 = $d3 | "4"; type $d5 = $d4 | "5"; type $d6 = $d5 | "6"; type $d7 = $d6 | "7"; type $d8 = $d7 | "8"; type $d9 = $d8 | "9"; interface $D_UMAP { 0: $d0; 1: $d1; 2: $d2; 3: $d3; 4: $d4; 5: $d5; 6: $d6; 7: $d7; 8: $d8; 9: $d9; } type $Max_1<T extends string> = "" | `${T}`; type $Max_2<T extends string> = $Max_1<T> | `${$Max_1<T>}${T}`; type $Max_3<T extends string> = $Max_2<T> | `${$Max_2<T>}${T}`; type $Max_4<T extends string> = $Max_3<T> | `${$Max_3<T>}${T}`; type $Max_5<T extends string> = $Max_4<T> | `${$Max_4<T>}${T}`; type $Max_6<T extends string> = $Max_5<T> | `${$Max_5<T>}${T}`; type $Max_7<T extends string> = $Max_6<T> | `${$Max_6<T>}${T}`; type $Max_8<T extends string> = $Max_7<T> | `${$Max_7<T>}${T}`; type $Max_9<T extends string> = $Max_8<T> | `${$Max_8<T>}${T}`; interface $Max_Map<T extends string> { 1: $Max_1<T>; 2: $Max_2<T>; 3: $Max_3<T>; 4: $Max_4<T>; 5: $Max_5<T>; 6: $Max_6<T>; 7: $Max_7<T>; 8: $Max_8<T>; 9: $Max_9<T>; } type $Repeat_1<T extends string> = `${T}`; type $Repeat_2<T extends string> = `${$Repeat_1<T>}${T}`; type $Repeat_3<T extends string> = `${$Repeat_2<T>}${T}`; type $Repeat_4<T extends string> = `${$Repeat_3<T>}${T}`; type $Repeat_5<T extends string> = `${$Repeat_4<T>}${T}`; type $Repeat_6<T extends string> = `${$Repeat_5<T>}${T}`; type $Repeat_7<T extends string> = `${$Repeat_6<T>}${T}`; type $Repeat_8<T extends string> = `${$Repeat_7<T>}${T}`; type $Repeat_9<T extends string> = `${$Repeat_8<T>}${T}`; interface $Repeat_Map<T extends string> { 1: $Repeat_1<T>; 2: $Repeat_2<T>; 3: $Repeat_3<T>; 4: $Repeat_4<T>; 5: $Repeat_5<T>; 6: $Repeat_6<T>; 7: $Repeat_7<T>; 8: $Repeat_8<T>; 9: $Repeat_9<T>; } // regexp: /[a-f]/ type $arg<From extends $a, To extends $a> = | Exclude<$A_UMAP[To], $A_UMAP[From]> | $A_MAP[From]; // regexp: /[0-9]/ type $drg<From extends keyof $D_UMAP, To extends keyof $D_UMAP> = | Exclude<$D_UMAP[To], $D_UMAP[From]> | $D_MAP[From]; // regexp: /T{From,To}/ type $rp< T extends string, From extends keyof $Max_Map<T>, To extends keyof $Max_Map<T> > = $Repeat_Map<T>[From] | Exclude<$Max_Map<T>[To], $Max_Map<T>[From]>; // examples: // regexp: /[5-9]/ const reg0: $drg<5, 9> = "7"; // regexp: /[b-e]/ const reg: $arg<"b", "e"> = "d"; // regexp: /a{2,6}/ const reg1: $rp<"a", 2, 6> = "aa"; // regexp: /\d{1,3}/ const reg2: $rp<$d, 1, 3> = "22"; // regexp: /[3-5]{1,3}/ const reg4: $rp<$drg<3, 5>, 1, 3> = "334";
const cssColor:
#${$rp<$d | $a, 3, 3>}= ""; 👉 no Problem
const cssColor:#${$rp<$d | $a, 3, 6>}= ""; 👉 [Expression produces a union type that is too complex to represent.(2590)"]
const cssColor:${$rp<$d, 4, 4>}= ""; 👉 no Problem
const cssColor:${$rp<"1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9", 5, 5>}= ""; 👉 no Problem
const cssColor:${$rp<$d , 5, 5>}= ""; 👉 [Expression produces a union type that is too complex to represent.(2590)"]
maybe maxLength? less than 100000, large than 45000, so typescript must't regexping string
(我搁这论猜想呢,咱就说,刚好让你不能搞好css 的颜色六位联合类型,超过最大值了家人们谁能懂 :))Reacted by klm127, Armen Bakir and Antoine ViallonReacted by Ricardo Fernández SerrataFor those who need type-safety on "predefined" attributes, here is what helped me:
To extendReact.HTMLAttributes:// global.d.ts declare module "react" { interface HTMLAttributes { "data-testid"?: string; "data-element-name"?: "first" | "second"; } }
In case you need to type every
data-*, you can do:// global.d.ts declare module "react" { type DataAttributeValue = number; interface HTMLAttributes { [`data-${string}`]?: DataAttributeValue; } }
Reacted by Mark Penner


There are cases, where a property can not just be any string (or a set of strings), but needs to match a pattern.
It's common practice in JavaScript to store color values in css notation, such as in the css style reflection of DOM nodes or various 3rd party libraries.
What do you think?