Assume inputting a sentence contains one or two spaces and has the structure ${Verb}${one or two spaces}${Activity}
, how could I extract the Verb
and Activity
in Typescript?
type Split = ' ' | ' '
type ExtractVerb<S extends string> =
S extends `${infer Verb}${Split}${infer Activity}`
? [Verb, Activity]
: never;
type Verbs = ExtractVerb<"play chess" | "write code" | "read hacker news">
I got the result type ["play", "chess"] | ["write", " code" | "code"] | ["read" | "read hacker", "hacker news" | "news"]
.
Expected: ["play", "chess"] | ["write", "code"] | ["read", "hacker news"]
.
CodePudding user response:
The reason for the behavior is that S
is a union, so any type containing it (such as ${infer Verb}${Split}${infer Activity}
) will be considered for both union members. So typescript will then give you both possible results. For "write code"
you can split by ' '
and get ["write"
, " code"
] or by ' '
and get ["write"
, " code"
].
You could keep the Split
strings in a tuple, and run through them until you get a match using a recursive conditional type:
type Split = [' ', ' ']
type ExtractVerb<S extends string, Seprators extends string[] = Split> =
Seprators extends [infer FirstSeparator, ...infer RestSeparators] ?
S extends `${infer Verb}${FirstSeparator & string}${infer Activity}`
? [Verb, Activity]
: ExtractVerb<S, RestSeparators & string[]>
: never
type Verbs = ExtractVerb<"play chess" | "write code" | "read hacker news">
Note that this is a tail recursive type, so the compiler should deal reasonably well with it.