Home > other >  How do I extend a type argument so that it allows only string literal types but not 'string
How do I extend a type argument so that it allows only string literal types but not 'string

Time:07-18

I need a type that is essentially "any string except for {reservedKeywords}". However, that is apparently either impossible or tricky and undwieldly.

I can settle for having to specify the string literals to use. This is as close as I can get to what I want, using Exclude<T,K>:

type AnySet<set extends string> = set
let test1: Exclude<AnySet<"a" | "1">, "1">
test1 = "a" //passes correctly
test1 = "1" //fails correctly

let test2: Exclude<AnySet<string>, "1">
test2 = "a" //passes correctly
test2 = "1" //passes (counterintuitively :( )

Is there any way I can prohibit passing string directly? Furthermore, is there a way to define a type is any collection of string literals but not specifically a string?

Here's what I mean:

type AnySet<set extends string> = set
let test1: Exclude<AnySet<"a" | "1">, "1">
test1 = "a" //passes correctly
test1 = "1" //fails correctly

let test2: Exclude<AnySet<string>, "1">
test2 = "a" //passes correctly
test2 = "1" //passes (counterintuitively)

type FiniteSet<set extends ???> = set
let test3: Exclude<FiniteSet<"a" | "1">, "1">
test3 = "a" //should work
test3 = "1" //should not work

let test4: FiniteSet<string> //should fail
let test5: FiniteSet<"a" | "b" | "c"> //should work

CodePudding user response:

You could use a recursive constraint (so-called F-bounded quantification) like S extends Exclude<string, S>:

type FiniteSet<S extends Exclude<string, S>> = S
let test3: Exclude<FiniteSet<"a" | "1">, "1">
test3 = "a" // okay
test3 = "1" // error

let test4: FiniteSet<string> // error
let test5: FiniteSet<"a" | "b" | "c"> // okay

This works because the Exclude<T, U> utility type filters unions in T to remove anything assignable to U. If T is string then the output will either be string (if string is a not a subtype of U) or never (if string is a subtype of U). String literal types are subtypes of string but not vice-versa, this gives the behavior you're looking for.

Playground link to code

  • Related