Assume that the following types are defined:
type A = {
a: string,
}
type B = {
a: number,
b: number,
}
These two assignments produce an error:
// Not assignable to A because 'b' doesn't exist on A
const x: A = {
a: 'hello',
b: 10
}
// Not assignable to B because 'a' expects type 'string'
const x: B = {
a: 'hello',
b: 10
}
TypeScript's documentation defines a Union Type as:
A union type is a type formed from two or more other types, representing values that may be any one of those types.
Based on this, I would have expected the following declaration to produce an error:
const x: A | B = {
a: 'hello',
b: 10
}
But it doesn't. The object literally is not assignable to type A and it's not assignable to type B, yet it is assignable to A | B.
How can I think of Type Unions in a way that makes this result make sense?
Editing: Of course, I search for half the day and can't find information that documents this, but as soon as I post, I find a number of other posts here talking about the same thing.
From what I found, an object literal can have excess properties when assigned as a Type Union, as long as one of the members has that property.
If I understand correctly, my object literal satisfies type A, with the property b being an excess property, which is allowed because another member of the union (B) includes the property.
Why does TypeScript's Structural Typing (i.e. "Duck Typing") necessitate non-strict Type Unions? From the number of SO posts I'm seeing relating to this, it seems like most people expect a Type Union to be strict. I'm sure that there's a compelling reason for non-strict unions to be the default, can someone provide some intuition for why that's the case?
From a type theory perspective, TypeScript uses structural typing ("if it acts like a duck, it's a duck") and allows additional properties. As a result:
Your
A | Bassignment works for similar reasons:TypeScript has additional handling for the specific case of object literals with an explicit type defined: since that's a common potential source of errors, it raises errors for that specific case. See Microsoft/TypeScript/#3755, which offers this more in-depth explanation from Anders Hejlsberg for the rationale:
There's a popular feature request for exact types, which could help enforce all of this more strictly, but it has not been implemented.
Now, if I understand correctly, TypeScript could extend its "not assignable" special case from Microsoft/TypeScript/#3755 to union literals: it presumably hasn't due to some combination of lack of demand, implementation complexity, or compiler performance hit. (I haven't searched GitHub issues to see if this has been requested.)