I have a series of type utilities which test whether a given type <T>
is the literal or wide variant of the type:
IsStringLiteral<T extends string>
IsNumericLiteral<T extends number>
IsBooleanLiteral<T extends boolean>
I then wanted to wrap these utilities into a single utility IsLiteral
which looks like the following:
export type IsLiteral<T extends string | number | boolean> = T extends string
? IsStringLiteral<T>
: T extends boolean
? IsBooleanLiteral<T>
: T extends number
? IsNumericLiteral<T>
: never;
I am running type tests on all of the individual utilities as well as my master blaster IsLiteral
and everything works ... except I get a false positive for booleans when I test IsLiteral<boolean>
and yet IsBooleanLiteral<boolean>
works as expected.
You can find info on the IsLiteral
here: [
CodePudding user response:
This is because boolean
is internally represented as true | false
. That means it'll be distributed in this conditional:
: T extends boolean
? IsBooleanLiteral<T>
and it'll now be:
(true extends boolean ? IsBooleanLiteral<true> : ...) | (false extends boolean ? IsBooleanLiteral<false> : ...)
which simplifies to:
IsBooleanLiteral<true> | IsBooleanLiteral<false>
both of which are true
, so you get true
for boolean
. You can avoid this by wrapping it in a tuple. This is described in distributive conditional types in the handbook:
: [T] extends [boolean]
? IsBooleanLiteral<T>