Skip to content

Treat string literals as readonly char arrays while compering them #51217

Closed
@DeepDoge

Description

@DeepDoge

Suggestion

🔍 Search Terms

string literal, template, complex, string literals as array

✅ 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.

⭐ Suggestion

I was just experimenting with this feature #40336
As mentioned in the PR:

Beware that the cross product distribution of union types can quickly escalate into very large and costly types. Also note that union types are limited to less than 100,000 constituents, and the following will cause an error:

We can't make the string literals too complex.
If we do we get this error: Expression produces a union type that is too complex to represent.
But this error is not caused because the type we wanna check is too complex.
It's cause because IDE generates every possible literal string type.

But instead of that we can treat strings as char arrays while compering them.
So no breaking changes, everything same but we compare string literals as if they are readonly char arrays
And after achieving this string unions can become less complex.

📃 Motivating Example

For example i wanna check if a string is a HEX color
I can write something like this

type Split<S extends string, D extends string> =
    string extends S ? string[] :
    S extends '' ? [] :
    S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] :
    [S]
type ExtractGeneric<T extends readonly any[]> = T extends readonly (infer U)[] ? U : never

const hexChars = 'ABCDEFabcef0123456789' as const
type HexChars = ExtractGeneric<Split<typeof hexChars, ''>>
type HexColor = `#${HexChars}${HexChars}${HexChars}` | `#${HexChars}${HexChars}${HexChars}${HexChars}${HexChars}${HexChars}`
const color: HexColor = "#fff"

But this is gonna give us this error: Expression produces a union type that is too complex to represent.
Because it tries to generate every possible string literal

But TypeScript is quite capable of checking if a string is a hex color.

type Split<S extends string, D extends string> =
    string extends S ? string[] :
    S extends '' ? [] :
    S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] :
    [S]
type ExtractGeneric<T extends readonly any[]> = T extends readonly (infer U)[] ? U : never

const hexChars = 'ABCDEFabcef0123456789' as const
type HexChars = ExtractGeneric<Split<typeof hexChars, ''>>
type HexColor = ['#', HexChars, HexChars, HexChars] | ['#', HexChars, HexChars, HexChars, HexChars, HexChars, HexChars]
const color = "#fff" as const
type Test = Split<typeof address, ''> extends HexColor ? true : false // Result: true

Example above is able to check if a string is a hex color or not.

So if we were to treat strings as readonly char arrays while compering them, the first example above would have worked without any issue.

💻 Use Cases

This can be use for many things.

  • Hex Colors
  • Zip Codes
  • 0x wallet addresses
  • URLs
  • etc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    UnactionableThere isn't something we can do with this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions