How do I refer to the "typeof T" in a generic function?

Viewed 145
function isSameOrSubclass<T>(target:any,reference:typeof T):target is T
{
    const   targetIsReference   = target===reference,
            targetIsTruthy      = target&&true,
            targetHasPrototype  = "prototype" in target,
            targetIsSubclass    = targetIsTruthy&&targetHasPrototype&&(target.prototype instanceof reference)
    return targetIsReference||targetIsSubclass;
}

Compiler fails with the following error:

TS2693: 'T' only refers to a type, but is being used as a value here.

Substituting typeof T with new()=>T allows compilation, but disallows this:

isSameOrSubclass<Class>(foo,Class)

because

Argument of type 'typeof Class' is not assignable to parameter of type 'new () => Class'.

1 Answers

T needs to be constrained to extends new() => {} *or similar and then used directly (not via typeof), like this:

function isSameOrSubclass<T extends new() => {}>(target:any,reference: T):target is T
// −−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−−−−−−−^
{
    const   targetIsReference   = target===reference,
            targetIsTruthy      = target&&true,
            targetHasPrototype  = "prototype" in target,
            targetIsSubclass    = targetIsTruthy&&targetHasPrototype&&(target.prototype instanceof reference)
    return targetIsReference||targetIsSubclass;
}

*or similar: the type new() => {} refers to classes with zero-argument constructor functions. Consequently, to accept classes with one-or-more-arguments constructors, a different type is necessary. i.e. new(...args) => {}

And then you use it without any explicit type parameter, like this:

class Parent {}
class Child extends Parent {}
class Unrelated {}
console.log(isSameOrSubclass(Child, Parent));      // true
console.log(isSameOrSubclass(Parent, Parent));     // true
console.log(isSameOrSubclass(Parent, Unrelated));  // false

Playground Link

Related