'Typescript template literals type force structure

I have the following code:

type Foo<T extends string = string> = `bar-${T}-foo`;

const v: Foo = 'bar-g-foo'

That works as expected, but it doesn't force the structure. The following is also valid:

  const v: Foo = 'bar--foo'

How can I force the usage of T?



Solution 1:[1]

The problem here is to check for an empty string. Read more about this problem here: How to write a string type that does not contain an empty string in TypeScript

Based on the stack from above you could do something like this:

type Character = 'a' | 'b' | 'c' | ... ; // Here all possible 1 letter strings
type NonEmptyString = `${Character}${string}`;
type Foo<T extends string = NonEmptyString> = `bar-${T}-foo`;

const v: Foo = 'bar-a-foo' // ?
const x: Foo = 'bar--foo' // ? Type '"bar--foo"' is not assignable to type

Playground link

Solution 2:[2]

It might not be the best approach, becasue of the reliance on the function assigning the value of generic type dynamically, but depending on your usage this could also be vallid

type NonEmptyString<T extends string> = T extends "" ? never : T;

function fn<T extends string>(a: `bar-${NonEmptyString<T>}-foo`) {
    // ...
}

fn("bar--foo"); // Error
fn("bar-g-foo"); // No error
fn("bar-gg-foo"); // No error

Playground link

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 T.J. Crowder
Solution 2 T.J. Crowder