This demonstrates a key aspect of TypeScript: assignability is not always transitive. Despite the general expectation of assignability being transitive, there are scenarios where this rule does not hold. For instance, consider types A
, B
, and C
such that A extends B
and B extends C
, yet A extends C
is false. This deviation from transitivity can lead to unexpected results within TypeScript due to its type system's limitations on soundness.
In a specific case like
const x: {} = 0;
, the assignment is allowed because the empty object type
{}
has no known properties that conflict with those of
0
. On the other hand,
const y: { [key: string]: string } = 0;
is disallowed as not every property of
0
is assignable to
string
.
Moreover,
const z: { [key: string]: string } = x;
is permitted since the anonymous empty object type
{}
receives an implicit index signature. While interfaces do not inherently receive implicit index signatures, non-interface object types exhibit characteristics of object literal types, enabling such assignments.
Though these behaviors may seem unsound, they align with TypeScript's design choices to balance safety and productivity for developers. Implicit index signatures offer convenience in coding practices despite potential conflicts with actual object properties matching index signatures. While some may challenge these decisions, TypeScript functions as intended in the examples discussed here, reflecting deliberate design rather than bugs.