1

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?

ken
  • 8,763
  • 11
  • 72
  • 133
  • What do you plan to pass into `Validator`... something like `Validator` presumably? If so, no there is no direct way to do this without higher kinded types as requested in [microsoft/TypeScript#1213](https://github.com/microsoft/TypeScript/issues/1213). (`IsBoolean` is not a first-class "thing" in TypeScript. It's a type function, but you can't refer to type functions in the abstract.) You could simulate higher kinded types with a bunch of boilerplate and annoyance, but nothing that would just work right out of the box. – jcalz Aug 22 '21 at 03:53
  • yes that's right @jcalz – ken Aug 22 '21 at 04:25
  • You could type this abstraction, you are just using the wrong tools. You could try doing something like `type Validator = (object: unknown) => object is T` and then use it like `const isBoolean: Validator = (object) => typeof object ==='boolean'`. You can type pretty much any validator like this, it's just that you need to change the way you think about them a bit, like using type guards instead of generics (like `IsBoolean`) everywhere – Alex Chashin Aug 22 '21 at 18:47
  • @AlexChashin That is a type guard and that works fine but fits a different use case. I am using type guards already and they work fine. This might be more apparent when you look at type literals like `true` instead of wider types like `boolean`. What is needed is a type utility to be applied and with wide types its typically just a simple _X extends Y_ operator. – ken Aug 22 '21 at 21:39
  • @AlexChashin I have updated the question to better describe what I'm talking about – ken Aug 22 '21 at 21:52
  • Ultimately I think @jcalz has naiiled down the problem I'm trying to solve ... not sure I have my head wrapped around the "work around" being proposed in that link but I'm going to try and understand it now. – ken Aug 22 '21 at 21:54
  • Sorry, I still don't quite get the problem you are trying to solve :) I could think more about your problem if you provided actual runtime code where you would like to apply this `Validator` abstraction, because when you are only writing types, I don't quite get why you would need `Validator` when you can just write `IsBoolean`. Or are you trying to type the types? Looks a bit weird – Alex Chashin Aug 23 '21 at 14:40
  • no problem; if you can't see the utility, I'm not going to try to talk you into it. – ken Aug 23 '21 at 18:58

0 Answers0