Repository navigation
Unable to create a function wrapperΒ #54569
Description
Activity
Okay so the problem here is that
Name extends keyof Testcan be a union of keys - in which casenameandargscan be mismatched1, so TS is warning you that the call is unsafe for that reason.You really need #27808 to deal with this properly. Short of that, youβll just have to ts-ignore the error.
Footnotes
-
Put more generally, it is not safe to call a union of function types with a union of their parameter types. β©
-
TS is warning you that the call is unsafe for that reason.
Nevertheless it does typecheck the calls correctly, so everything works exactly as I expect it to, except the error message.
Yes, but this call also typechecks:
run("one" as "one" | "two", 123);
β¦which therefore makes the inner call to
fnunsafe. Hence the error message.- Okay, so how do I create a wrapper function that preserves the static analysis?
I'd say #47109 is the current way to express this sort of thing:
type TestArgs = { [K in keyof Test]: Parameters<Test[K]> } type TestRet = { [K in keyof Test]: ReturnType<Test[K]> } const test: { [K in keyof Test]: (...args: TestArgs[K]) => TestRet[K] } = new Test(); export function run<K extends keyof Test>(name: K, ...args: TestArgs[K]): void { const fn = test[name]; const ret = fn(...args); // ^? const ret: TestRet[K] }
- TS doesn't provide any facilities to ignore this particular error that I know is irrelevant to me.
Could you elaborate? On the face of it it looks false: TS provides type assertions, the
anytype, and the//@ts-ignoredirective, each of which could be used to prevent your code from complaining. I'm not recommending you use any of those, but I'm wondering why they don't count as "facilities to ignore" your error.run("one" as "one" | "two", 123);
β¦which therefore makes the inner call to
fnunsafe. Hence the error message.I'm afraid I don't understand what is unsafe here. It correctly points out that you can't run one with a number.
I'd say #47109 is the current way to express this sort of thing:
I am once again finding myself at the bottom of the confidence curve. Hats off, this seems to check out.
- TS doesn't provide any facilities to ignore this particular error that I know is irrelevant to me.
Could you elaborate? On the face of it it looks false: TS provides type assertions, theanytype, and the//@ts-ignoredirective, each of which could be used to prevent your code from complaining. I'm not recommending you use any of those, but I'm wondering why they don't count as "facilities to ignore" your error.
Which one of these can tell TS to ignore this particular error and nothing else, again?
@ts-ignoreignores the whole line, so other errors can slip in. I run strict, so anyanyis an error in itself. I tried to assert the type of...args, but came back empty-handed too.- TS doesn't provide any facilities to ignore this particular error that I know is irrelevant to me.
I'm afraid I don't understand what is unsafe here. It correctly points out that you can't run one with a number.
It does not: See Playground
Joe Calzaretta (@jcalz) Worth noting that your solution suffers from the exact same unsoundness as shown here, despite removing the error in the implementation. This pattern really does beg for
oneofconstraints.It does not
I see the problem now. Strange how
run('one', 123)gives an error, butrun('one' as keyof Test, 123)doesn't, despite the same type being declared in the function argument. My TS knowledge is not enough to reason about what's going on at this point.As it's my own code, this solution still seems perfectly safe for my particular use case.
In the first case,
nameis typed as"one"; in the second case it's typed askeyof Test. That's the difference.By giving a union of keys for
Name, you get a union of function types forTest[Name]. Then, becauseParametersis distributive, it resolves to a union of those functions' parameter types. TS doesn't understand that these unions are all supposed to correlated (there's a separate issue about that, fwiw). By passing a string literal directly, it infers a string literal type forNameinstead of justkeyof Test, TS then knows exactly which functionTest[Name]refers to, and you get your nice type checking on the call.In the first case,
nameis typed as"one"; in the second case it's typed askeyof Test. That's the difference.That actually makes sense. Thank you.
I suppose I should go ahead and close the issue now.
Joe Calzaretta (@jcalz) Worth noting that your solution suffers from the exact same unsoundness as shown here, despite removing the error in the implementation. This pattern really does beg for
oneofconstraints.Yeah, see #48730
Reacted by Bruce Pascoe- locked as resolved and limited conversation to collaborators
on Oct 22, 2025
Bug Report
π Search Terms
2556, tuple, union
π Version & Regression Information
n/a
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
TypeScript shows an error about the spread argument.
I've seen a bunch of similar issues, however I left unsatisfied with them. My specific concerns are these:
run('five')run('one')run('one', 123)run('ten', null, window)as demonstrated in the last lineI'm left with no options, and I think this deserves an issue.
π Expected behavior
TypeScript doesn't show an error.