The main reason behind the success of this method lies in the capability of an intersection type to be assigned to its base types.
When it comes to an intersection type, Examples
can easily be assigned to ExampleA
. Furthermore, ExampleA
can be assigned to { [key: string]: string }
. Consequently, Examples
is certainly assignable to the function's parameter type.
The above explanation is exemplified in the following code snippet:
const bar: Examples = { a: 'foo', b: 1, c: false };
const bar2: ExampleA = bar;
const bar3: { [key: string]: string } = bar2;
foo(bar3); //It works
foo(bar2); //Since bar3 = bar2 works, this must work as well
foo(bar); //Since bar2 = bar works, this must work too
Playground version
UPDATE
The concept becomes significant when you want to follow the principle that states "when A is assignable to B and B is assignable to C, then A must be assignable to C". The type system is designed to allow such assignments. However, there is a potential issue when passing the value as a parameter to foo
.
You can assign a value to a variable of a type that shares only a subset of the members of the assigned value. For instance, this assignment is valid:
let item: { a: string, b: number } = { a: "Hello World!", b: 1 };
let partiallyMatchingItem: { a: string } = item;
Having more properties in partiallyMatchingItem
than declared in the type is completely acceptable. The guarantee is minimal.
However, the assignment to a mapped type fails because of the additional member of type number
in item
:
let item = { a: "Hello World!", b: 1 };
let mappedTypeItem: { [key: string]: string } = item; //Error
This time, the guarantee is not minimal but absolute. It can be easily bypassed intentionally or accidentally:
let item = { a: "Hello World!", b: 1 };
let partiallyMatchingItem: { a: string } = item;
let mappedTypeItem: { [key: string]: string } = partiallyMatchingItem;
Or simply:
let item = { a: "Hello World!", b: 1 };
let mappedTypeItem: { [key: string]: string } = item as { a: string };
This potential error is looming, especially when iterating through the properties of
mappedTypeItem</code assuming all values are of type <code>string
.
Considering the prevalent use of structurally typed assignments in TypeScript, this absolute guarantee contradicts the system of minimum guarantees provided by the type system.
A feasible solution would be to prevent values of "regular" types from being assigned to mapped types (backwards compatibility can be toggled with a switch in the tsconfig.json
file). It is advisable to avoid such assignments as the type safety offered here is weak.