This is going to be a bit of a journey so hang in there with me:
Generally a sub-type should be assignable to a base-type. In Typescript a type with more properties should be assignable to a where a type with just a subset of the properties is expected. So this for example is legal:
let source = { a1: 0, a2: 0}
let target: { a1: number } = source
Now surprisingly, because of the way structural typing works Partial<A>
is a subtype of Partial<B>
and Partial<B>
is a subtype of Partial<A>
. Optional properties can be missing in a subtype, so optional properties missing from a type do not disqualify a type from being a subtype. An if we remove the optional properties we are just left with {}
which can be a the base type of any other type. The compiler agrees with me on this point if we ask it to answer this subtyping question using conditional types:
type q1 = Partial<A> extends Partial<B> ? "Y" : "N" // Y
type q2 = Partial<B> extends Partial<A> ? "Y" : "N" // Y
There is one exception to this (ok maybe two), assignment of object literals directly to a reference of a particular type. This is called excess property checks as is the reason that if we were to perform the above assignment directly we would get an error:
let target: { a1: number } = { a1: 0, a2: 0} // err
The reason this is an error is that it is a common error to mistakenly create an object literal with more properties or misspelled properties and this check (which is a violation of the principle a sub-type should be assignable to a base-type) is there to catch this mistake. This is also the reason you are getting an error on
let partialA: Partial<A> = {b1: 1, x1: 1};
But excess property checks only kick in on direct assignment of an object literal to variable of a specific type. So on the assignment let partialA: Partial<A> = b;
it will not trigger an error on excess property checks.
One further complication is that Partial<A>
is what is called a weak type. From the PR introducing checks for such type:
A weak type is one which has only optional properties and is not empty. Because these types are assignable from anything except types with matching non-assignable properties, they are very weakly typechecked. The simple fix here is to require that a type is only assignable to a weak type if they are not completely disjoint.
Now since a weak type has no mandatory properties, following the principle a sub-type should be assignable to a base-type, any other object would be assignable to such a type:
let k = { foo: 0 }
let partialA: Partial<A> = k;
The type of k
is a sub-type of Partial<A>
, sure k
has nothing in common with Partial<A>
but that is not an issue. After all Partial<A>
does not require any propertie, so k
does not need any, and it has one extra property, which is generally what a sub-type does, become more specific by adding members.
The reason the code code above is an error is because of the PR are reference above which postulates that assigning a completely unrelated type to a weak type is probably an error. So like excess property checks a rule was introduced (a rule which again breaks the idea a subtype is assignable to a base type) that disallows such assignments.
In your case however Partial<A>
and Partial<B>
do have something in common x1
so this rule does not catch the possibly mistaken assignment. Although surprising because both Partial<A>
and Partial<B>
have no mandatory properties, they are subtypes of one another and unless there is a specific reason (such as excess property checks or this extra weak type detection rule) the assignment is allowed.