#Higher-order function only accepts a function value whose parameters are a spread tuple

20 messages · Page 1 of 1 (latest)

vital eagle
#

I have a higher-order function, annotate. Its purpose is to assign types to the parameters of the anonymous function it wraps, so that that function does not need its own type annotations. (The shape of { a: number, b: string } is arbitrary and not important here.)

function annotate<
    F extends <T extends { a: number; b: string }[]>(...args: [...T]) => any,
>(f: F): F {
    return f;
}

(The second "extends" is necessary to get TypeScript to infer the return type rather than assigning it outright.)

However, for some reason, "annotate" only works when the function parameter is a spread tuple:

annotate((...[{ a, b }]) => a);

If we provide the parameter directly, we get the error Target requires 1 element(s) but source may have fewer.

annotate(({ a, b }) => a);
``` (causes a type error)

Why is this happening? How can I make it work with a plain parameter?

The solution I'm looking for should still allow TypeScript to infer the return type:
```typescript
const f = annotate((...[{ a, b }]) => a);
const r = f({ a: 10, b: "abc" }); // typeof r should be "number"

It should should also still accept values that extend the expected type:

const f = annotate((...[{ a, b }]) => {});
f({ a: 10, b: "abc", c: true }); // Should be no error here
bold fieldBOT
#
catgirl_coded#0

Preview:ts // I have a higher-order function, "annotate". Its purpose is to assign types to the parameters // of the anonymous function it wraps, so that that function does not need its own type annotations. // (The shape of { a: number, b: string } is arbitrary and not important here.) ...

tight smelt
#

Hello.

vital eagle
#

Answered elsewhere! Solution was to replace the type parameter for annotate with:

F extends <T extends { a: number; b: string }>(...args: [T, ...T[]]) => any
tight smelt
#

Oh

#

Im glad you had a solution

vital eagle
#

Did you have one too? I'd still be interested to hear it

tight smelt
#

I was thinking of using conditional types and inference in mapped types. Something like this

type Params<T extends { a: number; b: string }> = T extends any[] ? [...T] : [T];

function annotate<
    F extends <T extends { a: number; b: string }[]>(...args: Params<T>) => any,
>(f: F): F {
    return f;
}

But as I see your solution is also good and shorter.

vital eagle
tight smelt
# vital eagle Actually, I might have to give this a try too. I need it to allow for *each* arg...

Oh, if you want to do that then you can modify the code a lil bit. Because the code I did enforces that all arguments must have the same type that extends {a: number; b: string } , so just modify the Params type and the function signature.

type Params<T extends { a: number; b: string }[]> = {
    [K in keyof T]: T[K] extends { a: number; b: string } ? T[K] : never;
};

function anootate<
    F extends (...args: Params<Parameter<F>>) => any
>(f: F): F {
    return f;
}

Something like this might work.

vital eagle
#

I'll give that a try!

tight smelt
vital eagle
tight smelt
#

Oh

#

Let me see

#

Oh mb

#

use parameters as these
"Parameters<F>"

#

inseatd of wrapping it

#

@vital eagle