Search code examples
typescriptstringunion-types

String replace when creating string literal union type in typescript


I have a string literal union type that looks like this:

type LowerCaseNames = 'youku-frame' | 'youku' | 'mini-program' | 'tiktok-frame';

This is only an example, since my union type is a list of more than 100 cases.

What I need to have is a new type where I would have the following "values" in type:

type UpperCaseNames = 'YOUKU_FRAME' | 'YOUKU' | 'MINI_PROGRAM' | 'TIKTOK_FRAME';

So far, I am using TypeScript's Uppercase type in order to have it upper-cased.

type UpperCaseNames = `${Uppercase<LowerCaseNames>}`;

What this returns is upper-cased union type but, I still have in them - instead of _. Is there a way for me to create a type that I can use like this ${Replace<Uppercase<LowerCaseNames>>} in order to replace - with _? I am using Typescript v4.4.4.


Solution

  • You can write a custom Replace<T, S, D> utility type which takes a string literal type T and produces a new version where occurrences of a source string S are replaced with a destination string D. Here's one way to do it:

    type Replace<T extends string, S extends string, D extends string,
      A extends string = ""> = T extends `${infer L}${S}${infer R}` ?
      Replace<R, S, D, `${A}${L}${D}`> : `${A}${T}`
    

    That's a tail-recursive template literal type which uses a type parameter A to accumulate the result. If T is of the form L+S+R for some (possibly empty) "left part" L and some (possibly empty) "right part" R, then you want to use L as-is, replace S with D, and then continue doing Replacements on R. Otherwise, T doesn't contain S and you just want to use T as-is.


    Let's try it out:

    type LowerCaseNames = 'youku-frame' | 'youku' | 'mini-program' | 'tiktok-frame';
    
    
    type UpperCaseNames = Uppercase<Replace<LowerCaseNames, "-", "_">>
    // type UpperCaseNames = "YOUKU" | "YOUKU_FRAME" | "MINI_PROGRAM" | "TIKTOK_FRAME"
    

    Looks good!

    Playground link to code