I have a few functions which do runtime and type checks for whether a given variable is of a particular type. For context, here's an example:
export type IsBoolean<T> = T extends boolean ? true : false;
export function isBoolean<T extends any>(i: T) {
return (typeof i === "boolean") as IsBoolean<T>;
}
So my real question is ... how does one type a utility type like IsBoolean<T>
? Being able to express this as a generalized type could be a powerful abstraction. I'm imagining something like:
export Validator<T, U> =
U<T> extends true
? true
: U<T> extends false
? false
: never;
This syntax does not work but is there a means to do this?
Addition:
The example above used a type check for Boolean, and like Boolean types most of the "wide" types use effectively the same sort of type utility:
type IsBoolean<T> = T extends boolean ? true : false;
type IsString<T> = T extends string ? true : false;
However, the pattern changes for other types such as for true
or even a base Function
:
// there are three states: true, false, and unknown
type IsTrue<T> = IsBoolean<T> extends true
? // is a boolean
T extends true
? true
: T extends false
? false
: unknown
: // not a boolean
false;
// runtime checks such as `X typeof ==="function"` test for both
// the simple function constructor as well as functions which have
// props but that means the type pattern for any function is:
type FunctionType = Function | (Function & {[key:string]:any})
type IsFunction<T> = T extends FunctionType
In the latter example, the introduction of FunctionType
allows us to move the base function example back into the first general pattern but with functions which have props you'll likely want strong typing on the props too (for a full validation anyway). This will move you away again from the general pattern.
There are many other examples of where a validator (which checks for type in both types and runtime) can be expressed as a type utility (aka, a type with a generic) but no single utility can do the trick.
This then leads to the heart of the question ... can we simply express a type as being a type utility so that multiple patterns can be injected into the generalized type?