-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Regex Validated String Types #21044
Regex Validated String Types #21044
Conversation
This is really neat, and a feature I will likely make heavy use of. But one aspect concerns me a bit, which is that I have to type my regexps twice. As regexps are essentially grawlix, this isn't necessarily the most trivial thing. Also having to keep the RegExps in sync when making changes is error prone. If there were some way to only create a RegExp once, say its value in normal JS, and get the corresponding regexp validated string type, I would be able to have a single source of truth for my regexps and it also might make it possible to use this feature with RegExps from validation libraries or the like. |
Right now the generic parameter for the declare global {
interface RegExp<T extends string = string> {
readonly " __dummyValidatedType": T;
}
} and then reference the type on any instance similarly to: const SimplePhoneNumber = /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/;
type SimplePhoneNumber = (typeof SimplePhoneNumber)[" __dummyValidatedType"]; AFAIK, there's no real instance field that'd exist at runtime on a |
This feature will be very useful for type-level programming. Combined with Conditional types, it will enable filtering of object properties by pattern. |
whats the status on this one? |
What a great feature! It would prevent a lot of errors about of UUIDs, Hexas, etc. |
This is so promising feature! With conditional types it looks even more powerful. //helper type to extrack "regex type" from RegExp<T>
type RegExpType<T> = T extends RegExp<infer U> ? U : never;
const SimplePhoneNumber = /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/;
type SimplePhoneNumber = RegExpType<typeof SimplePhoneNumber>
//some type we want to filer properties for
interface SomeType {
a: number;
b: string;
_privateProp: string;
_otherPrivateProp: { foo: string };
}
type FilteredSomeType1 = Pick<SomeType, Exclude<keyof SomeType, "_privateProp" | "_otherPrivateProp">>; //aleady works in TS 2.8
type FilteredSomeType2 = Pick<SomeType, Exclude<keyof SomeType, /_.*/>>; //Hope it will work in future 😍 |
Closing since #6579 isn't looking too likely in the near-term |
Per conversation following #41160 (comment) could the syntax be improved with a explicit type cast? |
This PR implements a subset of the proposals discussed in #6579.
Regular Expression Validated String Literal Types
These types enable users of regular expressions to receive automatic tracking of refinements on string types through type guards on strongly typed
RegExp
s. These refinements can compose via|
and&
like one would expect, and allow a user to limit the strings they take or return to a specific subdomain of strings, without listing all the members explicitly (which may even be impossible).It looks like this:
Syntax
A js regular expression in a type position is a regular expression validated string type.
The type of a js regular expression elsewhere is generic over the regular expression type it validates. (eg
var a = /a/
has typeRegExp</a/>
)The
.test
method ofRegExp
is a typeguard (test(s: string): s is T
).Assignability
A regular expression validated string type is
🚲 🏠 Should we refuse to execute regexes with more features we don't like (due to portability or likelihood to misuse)? (similarly to how flags other than `i` or `m` currently cause the regex not to be executed?) Like lookbehind, etc? Requires more work to explicitly prohibit or allow only certain classes of regexp to be executable (namely actually parsing the regex, something we currently refrain from doing), for only a small gain in perceived consistency - the current runtime reliance will only be noticeable when a user moves their project between seriously different compiler runtimes and has non-portable regex syntax.StringLike
. Two regular expression validatedstring types are equal only if they are textually equivalent. Any string literal type which matches
the given regexp is assignable to the type. If the regular expression cannot be executed (eg, it references
any flags other than
i
orm
, or errors on construction), no string literal types are directly assignable to it, and must be cast. I've included a quickfix for adding such a cast where it makes sense - it might not always be correct to cast (since we're not checking the literal's contents), but it should make it easy for the user to add the cast when it is needed.Examples
Future Work
Support index signatures containing arbitrary stringlike types, to allow for index signatures of regex validated string types, eg:
Complementary Proposals
unique
keyword on any typetype Zip = /^[0-9]{5}$(Zip)?/; type Digits = /^[0-9]{5}$(Digits)?/
- these match the same language, but are textually different, so become distinct types.Fixes #6579