Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define a Typescript string type of comma separated union types?

Tags:

typescript

How do you create a Typescript string type, which includes values from a union type AND is separated by commas?

I think this doesn't exist, but I'm asking anyway...

type AcceptableVal = 'cool' | 'wow' | 'biz' | 'sweet' | 'yowza' | 'fantastic';
type Amaze = // how do you define this?;

const dabes: Amaze = 'cool,wow';
const fancy: Amaze = 'biz,wow,fantastic';
const hachiMachi: Amaze = 'yowza,biz,sweet';
like image 464
Ari Avatar asked Dec 07 '25 04:12

Ari


2 Answers

Typescript 4.3.4 Update

You can now use string template literal types to define a pattern like:

type S = 'cool' | 'wow' | 'biz' | 'sweet' | 'yowza' | 'fantastic';
type Test = `${S},${S}`

What you still can't do is make this infinitely extensible, like an array. To make this work typescript generates every single combination as a union. But for small lists like this it can work.

For example:

type S = 'cool' | 'wow' | 'biz' | 'sweet' | 'yowza' | 'fantastic';
type Amaze =
    | `${S}`
    | `${S},${S}`
    | `${S},${S},${S}`
    | `${S},${S},${S},${S}`
    | `${S},${S},${S},${S},${S}`
    | `${S},${S},${S},${S},${S},${S}`

If you hover over Amaze, you will see the type reported as:

type Amaze = S | "cool,cool" | "cool,wow" | "cool,biz" | "cool,sweet" 
| "cool,yowza" | "cool,fantastic" | "wow,cool" | "wow,wow"
| "wow,biz" | "wow,sweet" | "wow,yowza" | "wow,fantastic"
| ... 55967 more ...| "fantastic,fantastic,fantastic,fantastic,fantastic,fantastic"

See typescript playground

Notice that ... 55967 more .... Amaze is now a union with over fifty five thousand possible values. This may affect performance in your IDE at this point. And if you add the version that takes 7 strings you'll get a type error:

Expression produces a union type that is too complex to represent.(2590)

Eventually typescript cuts you off for performance sake. But again, for small lists, this may be viable.


Original answer: Typescript 3.7

You can't.

Typescript can type strings which can have any content, or can it can type exact strings like "cool" or "wow". But typescript will never know about if a string contains certain characters.

The only way to make this work would be to store these as an array instead:

type AmazeArray = AcceptableVal[];
const dabes: AmazeArray = ['cool', 'wow'];
like image 83
Alex Wayne Avatar answered Dec 08 '25 21:12

Alex Wayne


with new template string feature of ts4.1 you can do this using following utils

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
    ? I
    : never;
type UnionToOvlds<U> = UnionToIntersection<U extends any ? (f: U) => void : never>;

type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;

type UnionConcat<U extends string, Sep extends string> = PopUnion<U> extends infer SELF
    ? SELF extends string
        ? Exclude<U, SELF> extends never
            ? SELF
            :
                  | `${UnionConcat<Exclude<U, SELF>, Sep>}${Sep}${SELF}`
                  | UnionConcat<Exclude<U, SELF>, Sep>
                  | SELF
        : never
    : never;

example

type Keys = "a" | "b" | "c";
type Test = UnionConcat<Keys, ",">
// Test = "a" | "b" | "c" | "a,b" | "a,c" | "b,c" | "a,b,c"


type Test2 = UnionConcat<Keys, "-">
// Test2 = "a" | "b" | "c" | "a-b" | "a-c" | "b-c" | "a-b-c"

like image 38
mh-alahdadian Avatar answered Dec 08 '25 19:12

mh-alahdadian