Describe the shape of an object

INPUT · Slides

Same shape, and it goes through

01 / 06

What it looks at is the shape, not the name

You have been naming shapes with interface, but what TypeScript looks at is the shape of the contents, not the name.

Try passing a plain object that never claimed to be a Person into a function that takes a Person. As long as the properties it needs are all there, it goes straight through.

interface Person {  name: string;}function callBy(p: Person): void {  console.log(p.name + "!");}const x = { name: "Yui" };callBy(x);

Result

Yui!

02 / 06

Two separately declared shapes swap freely

Two types with different names still swap over, as long as the contents have the same shape.

The name of a type is only something to call it by while a human reads it; deciding whether it fits is done entirely by the shape. This way of deciding is called structural typing.

interface Person {  name: string;}interface Guest {  name: string;}const g: Guest = { name: "Rin" };const p: Person = g;console.log(p.name);

Result

Rin

03 / 06

Missing something and it will not go through

Looking at the shape does not mean being lax. Anything incomplete does not go through.

Pass an object with no age into a Person parameter and it stops on the line where you passed it. The error even tells you which property is missing, so just read it.

interface Person {  name: string;  age: number;}function callBy(p: Person): void {  console.log(p.name);}const x = { name: "Yui" };callBy(x);

Result

Type error: Argument of type '{ name: string; }' is not assignable to parameter of type 'Person'.
  Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.

04 / 06

Having extra is fine

Missing is no good, but extra causes no trouble.

age is not a property of Person. Even so, name is there, so everything callBy wants to do still works, does it not? So it goes through.

interface Person {  name: string;}function callBy(p: Person): void {  console.log(p.name);}const x = {  name: "Yui",  age: 20,};callBy(x);

Result

Yui

05 / 06

And yet, written on the spot, it stops

The contents are the same, but change how you write it and the answer changes. What went through when it came from a variable gets stopped when you write it straight inside the brackets.

It looks like being picked on, but there is a good reason.

interface Person {  name: string;}function callBy(p: Person): void {  console.log(p.name);}callBy({ name: "Yui", age: 20 });

Result

Type error: Object literal may only specify known properties, and 'age' does not exist in type 'Person'.

06 / 06

Why does the way you write it matter?

Here is the reason. An object written on the spot can never be used anywhere else. If you wrote 20 for age and Person does not take it, that 20 goes nowhere. Whoever wrote it surely expected it to arrive — so it is probably a typo, and TypeScript stops you. This is called the excess property check.

A variable is different. That variable might be used somewhere else too, so carrying extra is not odd at all. So it goes through.

What it matches on is the shape — except that anything written on the spot gets looked at strictly. Let us write some.