-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lossy argument names when spreading across parameter types #48049
Comments
Actually, this workaround didn't work for my case either. Here's the same workaround wrapped in a type Equality = (a: number, b: number) => boolean;
type WithMoreParams<F extends (...args: any) => any> = (...args: [...Parameters<F>, ...Parameters<(lol: string) => void>]) => void;
type BAD = WithMoreParams<Equality>; As soon as you're doing it from a function declaration or another type expression, it loses the names again. |
If you're interested in the real-world use-case, here it is: export type Fact = {
pass: boolean;
actual: unknown;
details: unknown[];
}
type Details = (...details: unknown[]) => void;
export function assertion<F extends (...args: any[]) => boolean>(assert: F)
: (...args: [...Parameters<F>, ...Parameters<Details>]) => Fact
{
return (...args: [...Parameters<F>, ...Parameters<Details>]) => ({
pass: assert(...args.slice(assert.length) as any),
actual: args[0],
details: args.slice(1),
});
}
const isEqual = assertion((a:number, b:number) => a === b);
const fact = isEqual(1, 2, "because reasons"); (it's a factory function in a test-framework - it takes a simple (on that note, being able to infer documentation would be extremely useful in this case as well.) |
Tuple either all have labeled elements or none. In your example none are. Much simpler and clear way type BAD = (...args: [...factoryParams: Parameters<Equality>, detals: string]) => void; |
I had no idea you could name tuple members! (is that a documented feature? I've never seen this before.) Using this approach does seem to work: type Equality = (a: number, b: number) => boolean;
type WithMoreParams<F extends (...args: any) => any> = (...args: [...params: Parameters<F>, ...details: unknown[]]) => void;
type BAD = WithMoreParams<Equality>; Curiously though, the compiler demands that I name Apparently, tuple members can only have names if the parent tuple has a name? But yes, this approach works for my use case as well: export type Fact = {
pass: boolean;
actual: unknown;
details: unknown[];
}
type Details = (...details: unknown[]) => void;
export function assertion<F extends (...args: any[]) => boolean>(assert: F)
: (...args: [...params: Parameters<F>, ...details: unknown[]]) => Fact // π fixed!
{
return (...args: [...Parameters<F>, ...Parameters<Details>]) => ({
pass: assert(...args.slice(assert.length) as any),
actual: args[0],
details: args.slice(1),
});
}
const isEqual = assertion((a:number, b:number) => a === b);
const fact = isEqual(1, 2, "because reasons"); Thanks @IllusionMH ! I do think there is room for some improvements here. Maybe the error messages could be more helpful? It's definitely not clear to me why the obvious solution in my original post only works until you wrap it in a type for reuse. Something seems inconsistent here? If there is some practical reason why this doesn't / can't / shouldn't work, perhaps the 5048 error message ("Tuple members must all have names or all not have names") should appear when this is attempted? |
|
The short answer is that the parameter -> tuple name feature is "best effort" (since nothing in the type system works/doesn't work depending on these names), and the effort to handle this particular case simply hasn't been done yet |
This follows [1], in order to keep the label of each argument. [1]: 33e4172 Related: - #1570 (comment) - microsoft/TypeScript#39941 - microsoft/TypeScript#48049
Bug Report
π Search Terms
argument names spread
,parameter names spread
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
As shown here, the argument names are lost in if the argument list expression involves any arguments without names:
π Expected behavior
It should infer the type of
BAD
as(a: number, b: number, args_2: string) => void
.π€ Workaround
Declaring an extra function type with the additional arguments seems to work:
Inlining a function type seems to work too:
So there are known workarounds, although I would say these are non-obvious - it took me a long time to figure this out, and it's just surprising how the second part of the parameter type expression seems to determine the outcome of the first part.
The text was updated successfully, but these errors were encountered: