Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions pages/Interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ In TypeScript, interfaces fill the role of naming these types, and are a powerfu
The easiest way to see how interfaces work is to start with a simple example:

```ts
function printLabel(labelledObj: {label: string}) {
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}

Expand All @@ -20,6 +20,7 @@ printLabel(myObj);
The type-checker checks the call to `printLabel`.
The `printLabel` function has a single parameter that requires that the object passed in has a property called `label` of type string.
Notice that our object actually has more properties than this, but the compiler only checks that *at least* the ones required are present and match the types required.
There are some cases where TypeScript isn't as lenient as we'll go over in a bit.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "... isn't as lenient, which we'll cover in a bit." is easier to read.


We can write the same example again, this time using an interface to describe the requirement of having the `label` property that is a string:

Expand Down Expand Up @@ -82,7 +83,7 @@ interface SquareConfig {
width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = {color: "white", area: 100};
if (config.color) {
// Error: Property 'collor' does not exist on type 'SquareConfig'
Expand All @@ -97,6 +98,61 @@ function createSquare(config: SquareConfig): {color: string; area: number} {
let mySquare = createSquare({color: "black"});
```

# Excess Property Checks

In our first example using interfaces, TypeScript was okay with giving a value with the type `{ size: number; label: string; }` to something that only expected a `{ label: string; }`.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Simplify "was okay with"
  2. Simplify to use an object literal directly instead of "a value with the type".

"... TypeScript let us pass { size: 10, label: "Size 10 Object" } to something that only expected a { label: string }."

We also just learned about optional properties, and how they're useful when describing so-called "option bags".

However, combining the two naively would let you to shoot youreslf in the foot the same way you might in JavaScript.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:yourself

For example, taking our last example using `createSquare`:

```ts
interface SquareConfig {
color?: string;
width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
}

let mySquare = createSquare({ colour: "red", width: 100 });
```

Notice the given argument to `createSquare` is spelled *`colour`* instead of `color`.
In plain JavaScript, this sort of thing fails silently.

One could argue that this program is correctly typed, since the `width` properties are compatible, there's no `color` property present, and the extra `colour` property is insignificant.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "You could argue ..." to be consistent with the style of the rest of the file.


However, TypeScript is more opinionated here, and takes the stance that there's probably a bug in this code.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can simplify this down to "However, TypeScript thinks that there is probably a bug in this code."

Object literals get special treatment and undergo *excess property checking* when assigning them to other variables, or passing them as arguments.
If an object literal has any properties that the "target type" doesn't have, you'll get an error.

```ts
// error: 'colour' not expected in type 'SquareConfig'
let mySquare = createSquare({ colour: "red", width: 100 });
```

Getting around these checks is actually really simple.
The best and easiest method is to just use a type assertion:

```ts
let mySquare = createSquare({ colour: "red", width: 100 } as SquareConfig);
```

Another approach, which might be a bit surprising, is to assign the object to another variable:

```ts
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);
```

Since `squareOptions` won't undergo excess property checks, the compiler won't give you an error.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think this should be our recommendation; the recommendation would be to either use cast or use a contextual type with union types for all possible variations.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And probably mention the compiler flag to disable the error reporting.


Keep in mind, for simple code like above, it's questionable whether you should really be trying to "get around" these checks.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify to "Keep in mind that for simple code you shouldn't really be trying to "get around" these checks."

For more complex objects literals that have methods and hold state, you may need to keep these techniques in mind, but a majority of excess property errors in user code are actually bugs.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra plural, should be: "object literals"

That means if you're running into excess property checking problems for something like option bags, you might need to revise some of your type declarations.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example or explanation? I'm not sure what you mean here.


# Function Types

Interfaces are capable of describing the wide range of shapes that JavaScript objects can take.
Expand Down