Given a function which receives two parameters of the same generic type:
declare const fn: <T>(t1: T, t2: T) => any;
I am trying to reason about the different behaviour TypeScript has when this function is invoked.
When the 2 parameters are both different primitives:
fn(1, 'foo'); // Error due to different types
When the 2 parameters are both different objects:
fn({ foo: 1 }, { bar: 1 }) // No error and return type is union of different types
How come these two usages don't have the same behaviour? I would expect them both to behave the same. Either:
Secondly, TypeScript's behaviour is different again if one of the parameters passed in is a variable (rather than an inline object literal):
fn({ foo: 1 }, { bar: 1 }) // No error and return type is union of different types
const obj = { foo: 1 };
fn(obj, { bar: 1 }) // Error due to different types
Again, how come these two usages don't have the same behaviour? I would expect both of these cases to behave the same.
The compelling use case for this behavior comes from examples like this, where you have some candidate set of types, none is a supertype of the others, but the intended inferred type parameter ({ a?: number, b?: number, c?: number }
) is pretty obvious:
declare const fn: <T>(t1: T, t2: T, t3: T) => any;
fn({ a: 0, b: 1 }, { b: 2, c: 3 }, { a: 4, c: 5 });
Why does this not happen when the arguments aren't object literals?
When you have some binding foo
with some type T
, TypeScript can't* know that the object pointed to by foo
has exactly the type T
- it might have more properties that weren't declared in T
, but ended up bound to foo
via a subtype relation. This is very common in practice.
For this reason, in the example in the OP, inferring the type { foo?: number, bar?: number }
would be unsound because obj
might have pointed to an object with a bar
property of type string
.
*
You could add more special cases around const
s which were initialized with an object literal that wasn't type-asserted to something else, but that would just make things even more inconsistent
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With