-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Type parameter cannot be inferred from another in simple case #35331
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
Comments
Try this: type Foo<T> = (t: T) => T
const increment: Foo<number> = x => x + 1
declare function acceptFoo2<F extends Foo<any>>(foo: F): void
acceptFoo2(increment) // Works
// ^ has type acceptFoo2<Foo<number>> (foo: Foo<number>): void |
@AlCalzone In my real code, I actually do need to use T, for example I might need it in a bound for another parameter: type Foo<T> = (t: T) => T
const increment: Foo<number> = x => x + 1
declare function acceptFooBar<T, F extends Foo<T>, S extends T>(foo: F, bar: S): S
acceptFooBar(increment, 2) // error The work-around works just fine here: type Foo<T> = (t: T) => T
const increment: Foo<number> = x => x + 1
declare function acceptFooBar<T, F extends Foo<T>, S extends T>(foo: F & Foo<T>, bar: S): S
acceptFooBar(increment, 2) ...which leads me to believe it could be inferred automatically. |
I do sometimes use type Foo<T> = { value: T }
type Bar<F extends Foo<any>> = { foo: F } // seems reasonable enough...
declare function acceptBar<F extends Foo<any>, B extends Bar<F>>(b: B): F
const result = acceptBar({ foo: { value: 123 } }).value
// uh oh, result is now `any`, taken from the `any` in line 2 |
The I'll have to dig through my projects to get specific examples. |
There should never be more type parameters than inference sites; parameter constraints are not inference sites and are intended to not be inference sites. Generally if you find yourself writing multiple type parameters like this to try to relate the return type to some facet of the input type, you should use a conditional type or lift/lower the type parameter's position in the input position instead. |
@RyanCavanaugh Thanks for your response. I think I understand what you are saying, basically that it's not the compiler's job to infer type parameters, but to infer things like argument types. Generally you should limit type parameters to the ones you need to describe the actual types of things, e.g. instead of:
you would write:
or, you can extract the type parameter you need when you need it using a conditional, like so:
For this particular simple function signature, the second of the three snippets is clearly best. My comments on this are:
while, perhaps amazingly, this does:
Another problem with conditionals is they always succeed. The convention is to use
I will keep the feedback in mind, and use fewer type parameters. The lack of a general way to write " |
There are cases where you have the same number of inference sites as type parameters and you still need the Maybe I should revise my claim and say that I remember using this trick, but can't actually find these usages anymore... |
This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow. |
TypeScript Version: 3.7.2 and 3.8.0-dev.20191123
Search Terms: Inference, generic function, unknown, argument not assignable, type parameter
Code
Expected behavior:
Inference results in
T
beingnumber
, notunknown
. No type error atacceptFoo2
call site.Actual behavior:
T
isunknown
, causing a type error.Note that if Foo were covariant in T, not invariant (as in this case) or contravariant, the above code would type-check, but the poor type for T could still be impactful, for example if T were part of the return type of
acceptFoo
. It's fortunate that the above work-around, using an intersection, is available. Without that, you'd have to specify all type parameters at the call site, because TypeScript doesn't yet support specifying some type parameters and inferring the rest. I am creating some APIs where it would be very inconvenient for the user to explicitly write out all the type parameters that would be inferred.For some reason I thought TypeScript's inference was smart enough to go through one type parameter to another, but I tried this code on versions back to 2.8.1 and the result is the same.
Playground Link: http://www.typescriptlang.org/play/?ts=3.8.0-dev.20191123&ssl=1&ssc=1&pln=12&pc=31#code/C4TwDgpgBAYg9nAPAFQHxQLxQBTAFxTICUm6yAsAFADGcAdgM7BQCWd1AThALYR36wEiOgFduAIwgd0WAB6ko8gNRQAjFSoATCNQA2AQy5QAZiPbAW9KPurUIYYPCRpsxhAScpURAgDc4LJpUNnYOTthsnDx8wCQA9HFQAOpwHADWDBqU2nqG0KbmlnTWtvaOCABMKAA0sFAQssB8mgyCzqioru6wPlD+gcGlYZUR7Fy8-PGJAKIcHKm1hgDmYjFQdHDM+gwMLEt0+uK60MBwbYhmaRsA7nSoWQnJqWkAtIZwZpoEDWC6LNQsYC6ECsfhSBg6ZgwKDXQEAC1YwFa4g+dCC2R0BiMBWoFisITKTgAzDU6g0mmjWp40J03HAPEI0FAAGQ9PwBdEE4ZwImjKITWJQR4pdIMIA
Related Issues:
Possibly related to #35288, which is about inference not taking advantage of the fact that a class Foo extends interface IFoo.
The text was updated successfully, but these errors were encountered: