Skip to content

Template strings: Negated match #49867

Open
@jamiebuilds

Description

@jamiebuilds

Suggestion

📃 Motivating Example

Strings, in particular key names, sometimes affect types based on the format of the string, most commonly with prefixes. One such example is the recent W3C Design Tokens spec which uses a $ prefix to reserve key names like $description and $type.

Right now it's possible to create a positive match such as:

type CssCustomPropertyName = `--${string}`

But there's no way to create a negative match. Or in regex terms:

(?!--)   # not `--`

The goal here wouldn't be to recreate all the functionality of regex/parsing, it would be to handle the stringy-typing sometimes seen in JavaScript (including within official web specifications).

⭐ Suggestion

Note: This is one option, I'm not particularly tied to it and could suggest alternatives if this is not workable.

Expose, in a limited capacity, the not operator (#29317) so that it can be used to filter strings.

type Name = string & not `--${string}`

let a: Name = "--x" // err!
let b: Name = "--" // err!
let c: Name = "x" // ok
let d: Name = "x--" // ok
let e: Name = "-x" // ok

There is an existing PR adding a not operator, but has been long stalled on expected behavior. But maybe just this one piece could be added and slowly expanded from, if desired, later.

💻 Use Cases

The W3C Design Tokens spec does not allow token/group names starting with a $ or containing any of the characters .{}.

Using negated string matches, you could correctly type this:

type Name = string & not `$${string}`

interface Group {
	$description?: string;
	$type?: string;
	[key: Name]: Token | Group
}

The closest you can get to this today is:

type LowerAlpha = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z'
type UpperAlpha = Uppercase<LowerAlpha>
type Numeric = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type AlphaNumeric = LowerAlpha | UpperAlpha | Numeric
type SafeSymbols = "-" | "_" | " "

type NameStart = AlphaNumeric | SafeSymbols
type Name = `${NameStart}${string}`

type Group = {
    $description?: string;
    $type?: string;
} & {
    [key in Name]: Group
}

let group: Group = {
    $description: "",
    $type: "",
    "my cool name": {}
}

🔍 Search Terms

List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback.

  • String
  • Negated
  • Pattern
  • Regex
  • Regular Expression
  • Not
  • Prefix
  • Suffix

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions