How do I narrow a class's types to only functions accepting no arguments?

Playground example

Let’s say I have this class:

class Nandar {
    stuff: string | null = null;
    doSomething(a: string, b: string): void {
        console.log(`${a},${b}`);
    }
    nothing(): void {
        console.log('nothing');
    }
}

I can use this type to extract the class’s properties, narrowing the keys to only a given function signature:

type Delegates<Type, Fn> = keyof {
    [K in keyof Type
    as Fn extends Type[K] ? K : never]: K
};

For example:

type StringDelegate = (a: string, b: string) => void;

const delegate: Delegates<Nandar, StringDelegate>; // delegate's only possible value is 'doSomething'

If I want to narrow to Nandar.nothing‘s type, though, I have a problem.

Using similar code:

type NothingDelegate = () => void;
const delegate: Delegates<Nandar, NothingDelegate>; // delegate's values can be 'doSomething' or 'nothing'

I don’t want ‘doSomething’; I want ‘nothing’ only. Basically Typescript takes () => void to refer to any function, using any number of arguments and any return type. That’s not what I want Typescript to do: I want it to refer to functions using no arguments and returning nothing.

The handbook says that

a void-returning callback type says "I’m not going to look at your
return value, if one exists"

But I DO want to look at the return value, and ensure that it is void. I also want to ensure that the function has no arguments. Is there syntax for that?

>Solution :

The problem is that for a function to be assignable to another, it doesn’t necessarily need the same arity. If a function’s parameter types matches another function’s, and the only difference is that the function takes less parameters, then it is assignable:

type UhOh = (() => void) extends ((a: string, b: string) => void) ? true : false;
//   ^? true

Here you can see that a function that takes no arguments is assignable to a function that takes two arguments. To make sure that the function has no arguments, you may use the Parameters utility type to check that it has no parameters:

type NoArgFns<T> = {
    [K in keyof T]: T[K] extends (...args: any[]) => any ? Parameters<T[K]>["length"] extends 0 ? K : never : never
}[keyof T];

type T = NoArgFns<Nandar>;
//   ^? "nothing"

Playground

Leave a Reply