Bug Report
When a generic type is constrained by another generic type, the inference doesn't extend to the second generic type unless it is explicitly included in the function signature. In short, <T, U extends I<T>>(_: U) will always infer unknown for T. I couldn't find any issues referencing this, but the behavior has to be known about. For most types, e.g. covariant ones, this isn't strictly incorrect, it just produces a wider type than a user might expect. However, for contravariant types, this will actually caused a rejection of valid code. Both instances can be "fixed" by manually specifying both types in the signature, e.g. <T, U extends I<T>>(_: U & I<T>) but this is clunky and may not be obvious to some people (i.e. me).
I could believe that this is also a design limitation, but given the semantics of extends, I would think it's possible to "insert" these extra intersections whenever a type is constrained by a generic. Most times it will be meaningless, but in this instance it will allow inference to pick up the appropriate type.
🔎 Search Terms
inference, bound, constraint
🕗 Version & Regression Information
- This is the behavior in every version I tried (3.3, 4.2, beta, nightly), and I reviewed the FAQ for entries about ________ (I'm not sure what should go in this blank, but I did not find any references in the FAQ)
⏯ Playground Link
Playground link with relevant code
💻 Code
Admittedly, but examples are a little contrived, but I have run into them in actual code, where I need references to both types.
Covariant example
interface Covariant<T> {
val: T
}
declare function covariantSimple<T, C extends Covariant<T>>(inp: C): { inp: C, inner: T };
covariantSimple({ val: { foo: 5 } }).inner.foo;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ object is unknown
// but object should / would normally be inferred to be of type { foo: number }
// manually specifying the intersection in the type tells typescript how to do the inference
declare function covariantIntersection<T, C extends Covariant<T>>(inp: C & Covariant<T>): { inp: C, inner: T };
covariantIntersection({ val: { foo: 5 } }).inner.foo; // no error
Contravariant example
interface Contravariant<T> {
(val: T): boolean;
}
declare function contravariantSimple<T, C extends Contravariant<T>>(inp: C): C;
contravariantSimple(({ val }: { val: boolean }) => val);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Argument of type '...' is not assignable to parameter of type 'Contravariant<unknown>'
// same error, but since the type is contravariant, failure to infer a narrower type for T causes a type error
// like before, manually specifying the intersection allows typescript to infer properly
declare function contravariantIntersection<T, C extends Contravariant<T>>(inp: C & Contravariant<T>): C;
contravariantIntersection(({ val }: { val: boolean }) => val); // no error
🙁 Actual behavior
When a generic type is constrained by another generic type, the inference doesn't extend to the second generic type unless it is explicitly included in the signature.
🙂 Expected behavior
The inference magically works!
Bug Report
When a generic type is constrained by another generic type, the inference doesn't extend to the second generic type unless it is explicitly included in the function signature. In short,
<T, U extends I<T>>(_: U)will always inferunknownforT. I couldn't find any issues referencing this, but the behavior has to be known about. For most types, e.g. covariant ones, this isn't strictly incorrect, it just produces a wider type than a user might expect. However, for contravariant types, this will actually caused a rejection of valid code. Both instances can be "fixed" by manually specifying both types in the signature, e.g.<T, U extends I<T>>(_: U & I<T>)but this is clunky and may not be obvious to some people (i.e. me).I could believe that this is also a design limitation, but given the semantics of extends, I would think it's possible to "insert" these extra intersections whenever a type is constrained by a generic. Most times it will be meaningless, but in this instance it will allow inference to pick up the appropriate type.
🔎 Search Terms
inference, bound, constraint
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
Admittedly, but examples are a little contrived, but I have run into them in actual code, where I need references to both types.
Covariant example
Contravariant example
🙁 Actual behavior
When a generic type is constrained by another generic type, the inference doesn't extend to the second generic type unless it is explicitly included in the signature.
🙂 Expected behavior
The inference magically works!