typescript - Wrap generic function - Stack Overflow

Is there a way to wrap a generic function inside a class and preserve the generics?Here is my idea:co

Is there a way to wrap a generic function inside a class and preserve the generics?

Here is my idea:

const func = <G extends string>(p: G) => p;

class Func<A extends any[], R> {
  constructor(private readonly func: (...args: A) => R) {}

  public call(...args: A): R {
    return this.func(...args);
  }

  public catch(cb: (error: unknown) => void) {}

  // more methods ...
}

Now if I use it the generics are obviously lost:

const use = new Func(func); // =>  Func<[p: any], any>

I have tried this, which preserves the functions type as a whole:

class Func<F extends (...args: A) => R, A extends any[], R> {
  constructor(private readonly func: F) {}

  public call(...args: A): R {
    return this.func(...args);
  }

  public catch(cb: (error: unknown) => void) {}
}

But even then, if I try to use the call method, generics (and even parameter types) are lost:

use.call('str'); // => any

To summarize: I need a way to wrap a generic function in a class and preserve its generics for the class methods. I am open for any creative workaround.

Playground: link

Is there a way to wrap a generic function inside a class and preserve the generics?

Here is my idea:

const func = <G extends string>(p: G) => p;

class Func<A extends any[], R> {
  constructor(private readonly func: (...args: A) => R) {}

  public call(...args: A): R {
    return this.func(...args);
  }

  public catch(cb: (error: unknown) => void) {}

  // more methods ...
}

Now if I use it the generics are obviously lost:

const use = new Func(func); // =>  Func<[p: any], any>

I have tried this, which preserves the functions type as a whole:

class Func<F extends (...args: A) => R, A extends any[], R> {
  constructor(private readonly func: F) {}

  public call(...args: A): R {
    return this.func(...args);
  }

  public catch(cb: (error: unknown) => void) {}
}

But even then, if I try to use the call method, generics (and even parameter types) are lost:

use.call('str'); // => any

To summarize: I need a way to wrap a generic function in a class and preserve its generics for the class methods. I am open for any creative workaround.

Playground: link

Share Improve this question asked Nov 20, 2024 at 11:45 Janek EiltsJanek Eilts 5724 silver badges12 bronze badges 5
  • 1 TS has very limited support for this kind of thing; it's implemented at microsoft/TypeScript#30215 but doesn't work with a class constructor this way. The workaround I'd use here is to do the F type and then declare that call is of type F, possibly like this playground link shows. Does that fully address the q? If so I'll write an a; if not, what's missing? – jcalz Commented Nov 20, 2024 at 12:32
  • Yes, thanks a lot! I guess there is no way to have a type assertion on class methods, then. I'll possibly use an arrow function instead. – Janek Eilts Commented Nov 20, 2024 at 13:39
  • Type assertion? I’m not using an assertion anywhere; do you mean “annotation”? – jcalz Commented Nov 20, 2024 at 13:39
  • No, your solution is perfect if I want the call method on the prototype. I just thought it would be cleaner if one could assert a class method like this: public call() {} as F, which isn't possible in TypeScript. However, it can be done via an arrow function, like this: ((...args: any[]) => {}) as F, but in that case, the method would be on the instance rather than the prototype. So, essentially, your answer is the best possible solution to my question. I was just thinking out loud. – Janek Eilts Commented Nov 20, 2024 at 13:52
  • playground – Janek Eilts Commented Nov 20, 2024 at 14:00
Add a comment  | 

1 Answer 1

Reset to default 1

Unfortunately TypeScript doesn't have much support for the sort of higher order manipulation of generic function types you're doing. The support it does have, as implemented in microsoft/TypeScript#30215 is quite limited, and doesn't apply when you are returning an object with the intended generic function as a method. So we can't leverage that, especially in the form where the function parameters and return type have separate type parameters (since that would break the generics).

Seeing as all you're trying to do is say that new Func(f).call is the same exact type as f, we can work around it by making Func generic in the type of f, and declaring call to be that type. You can't do that with method syntax (there's an open feature request at microsoft/TypeScript#22063 to allow annotating a function statement and, presumably, a method, so it's a missing feature). You can use declaration merging to declare it as if it were a property, and then manually add call to Func.prototype, with some workaround to allow you to access a TypeScript-private property inside of it (see microsoft/TypeScript#19335):

// class is generic in type of `func`:
class Func<F extends (...args: any) => any> {
  constructor(private readonly func: F) { }
  public catch(cb: (error: unknown) => void) { }
}

// merge in `call` as same type
interface Func<F> {
  call: F;
}
// implement `call`
Func.prototype.call = function (this: Func<any>, ...args: any) {
  return this["func"](...args); 
  //         ^^^^^^^^ brackets let you access private member
}

And now if we try it we see the desired behavior:

const func = <G extends string>(p: G) => p;
const use = new Func(func);
const s = use.call('str');
//    ^? const s: 'str'

So it's possible to do what you asked for in the question... but the solution is fragile. Only because call is exactly the same type as this.func does it work; if you needed something even slightly different that would need the type to be transformed in some way (e.g., the function returns something like {prop: this.func(...args)}, or accepts an extra or fewer argument compared to this.func, etc) then you'd end up with broken generics again.

Playground link to code

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742360673a4429328.html

相关推荐

  • typescript - Wrap generic function - Stack Overflow

    Is there a way to wrap a generic function inside a class and preserve the generics?Here is my idea:co

    2小时前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信