question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

Template strings: Negated match

See original GitHub issue

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.

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:14
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
jamiebuildscommented, Jul 14, 2022

Thinking about this more, I wonder if a lot of the concerns about the original not proposal could be addressed by starting with a more limited feature only for primitive types and literals:

type NonEmptyString = string & not ""
type NonZeroNumber = number & not 0
type NonSymbolPropertyKey = PropertyKey & not symbol
interface Nums {
  label: string
  [index: string & not "label"]: number
}
interface DesignTokens {
  $type: string
  $description: string
  [index: string & not `$${string}`]: any
}
  • Allowed: Literals (true, 0, "", `${}`, etc
  • Allowed: Primitives (null, boolean, number, bigint, string, symbol)
  • Banned: Everything else (any, never, unknown, objects, etc)

From the original pull request (#29317), from what I can gather, these were the primary concerns: (cc @DanielRosenwasser)

  1. Intuition: Some use-cases donā€™t behave intuitively
// (this is an error because objects in TS are not 'exact')
let o: not { bar: string } = { foo: string } // Error!
  1. Consistency: Can this feature compose with every other feature in TS and get expected results?

  2. Complexity: This is going to have to be considered in every future change to TypeScript, and could get in the way of more useful features.

  3. Use cases: A lot of the use-cases for this feature can already be implemented in other (less declarative) ways

Flexibility around primitive and literal types is the main use case that Iā€™m concerned with, and I think I would actually prefer if TypeScript told me when I was trying to do silly stuff like not any because that sounds like a mistake to me.

1reaction
RyanCavanaughcommented, Jul 12, 2022

If we presuppose not T as the type negation operator, then itā€™s tempting to support this (initially) via

type Name = `${not "--"}${string}`;

which we can parse only in the context of a ${ ... } placeholder. Thoughts?

Read more comments on GitHub >

github_iconTop Results From Across the Web

How To Negate Regex - preg match - Stack Overflow
Match as in search, or validation? For validation, you can use the same string, but use negation to the result of the matching...
Read more >
Regular expressions - JavaScript - MDN Web Docs
Regular expressions are patterns used to match character combinations in strings. In JavaScript, regular expressions are also objects.
Read more >
Regular Expression Language - Quick Reference
A pattern has one or more character literals, operators, ... [^ character_group ], Negation: Matches any single character that is not inĀ ...
Read more >
re ā€” Regular expression operations ā€” Python 3.11.1 ...
A regular expression (or RE) specifies a set of strings that matches it; ... Regular expressions can be concatenated to form new regular...
Read more >
Regular expression syntax reference | IntelliJ IDEA ... - JetBrains
n is a non negative integer. Matches exactly n times. For example, o{2} does not match the o in Bob, but matches the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found