NoInfer not working on callback parameter destructuring
miguel-leon opened this issue Β· 13 comments
π Search Terms
NoInfer parameter callback destructuring
π Version & Regression Information
Latest to date.
β― Playground Link
π» Code
class Foo<T, R> {
constructor(readonly cb: (t: T, _: NoInfer<{ x: number; other: NoInfer<R> }>) => R) {}
}
const _1 = new Foo((name: string, { x }) => ({ name, x })); // Foo<string, unknown>
// should be Foo<string, { name: string; x: number }>
const _2 = new Foo((name: string) => ({ name })); // works: Foo<string, { name: string }>
π Actual behavior
infers unknown
.
π Expected behavior
Should ignore the parameter tagged with NoInfer
and infer correctly.
Additional information about the issue
Is this intended behavior? feels like a bug to me.
If the callback parameter is destructured partially it infers unknown
, but it works if the parameter is omitted.
I tried NoInfer
at various locations.
Note: not a TS team member
That parameter is not annotated so it can't infer from there anyway, with or without NoInfer
. I think you're hoping that NoInfer
can somehow break the dependency of the function return type on its parameter type, but that's not what NoInfer
does. This looks more like #47599 and possibly even just a plain old design limitation.
I don't know what you mean here by "not annotated" and I'm not hoping anything about NoInfer
, let alone "break the dependency" which I also don't know what you mean.
If this looks ok to you, π.
{ x }
is not annotated with a type. Therefore you are expecting it to be contextually typed. Since there's no type there, TypeScript cannot possibly infer the type from that position, whether or not you have NoInfer
there.
As for "I'm not hoping anything" I was trying to say that you have an expectation about NoInfer
that is not warranted. In the type (t: T, _: NoInfer<{ x: number; y: NoInfer<R> }>) => R
the type of _.other
depends on R
and so does the return type. When you pass in a function like (t: string, other) => β―
, TS wants to know the type of the return type before it knows what other
is, but since, syntactically, the return type might well depend on the type of y.other
, it can't do that and it fails. Your expectation here is that TS could possibly inspect the body of the function (the β―
) and see that the return type actually does not depend on the type of y.other
. But TS doesn't do this. See #47599 and the issues linked to that one.
In this example { x }
is "not annotated" like you say but it's still inferred. So I still don't know what you mean.
As for you insisting in my expectations, like I say, I don't have any. Here is explained what NoInfer
does, and I'm using it for the explained purpose.
If you're trying to say this is some sort of an undocumented gotcha (for other reasons or design limitations), then ok, just say it can't be done. No need to discuss my expectations.
In any case, not wanting to continue the discussion above and getting back to the issue at topic.
I managed to do a workaround that works. playground
class Foo<T, R> {
constructor(readonly cb: <NoInferR = R>(t: T, _: { x: number; other: NoInferR }) => R) {}
}
const _1 = new Foo((name: string, { x }) => ({ name, x })); // Foo<string, { name: string; x: number }>
but it breaks again as soon as I try to merge the class with an interface. It doesn't make sense. playground
class Foo<T, R> {
constructor(readonly cb: <NoInferR = R>(t: T, _: { x: number; other: NoInferR }) => R) {}
}
interface Foo<T, R> {}
const _1 = new Foo((name: string, { x }) => ({ name, x })); // Foo<string, unknown> again
Iβ¦ the βexpected behaviorβ is all I was trying to say (as per the template for the issue) I sincerely donβt mean to offend, nor do I want to seem to derail your issue. I will disengage now. Good luck.