Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #211 from Microsoft/indexSigs
Browse files Browse the repository at this point in the history
Suggest index signatures for excess properties/update index signatures.
  • Loading branch information
DanielRosenwasser committed Apr 14, 2016
2 parents 69f6ec5 + 514a67c commit 330c41e
Showing 1 changed file with 46 additions and 11 deletions.
57 changes: 46 additions & 11 deletions pages/Interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,21 +134,33 @@ 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:
The easiest method is to just use a type assertion:

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

Another approach, which might be a bit surprising, is to assign the object to another variable:
However, a better approach to might to add a string index signature if you're sure that the object can have some extra properties that are used in some special way.
If `SquareConfig`s can have `color` and `width` properties with the above types, but could *also* have any number of other properties, then we could define it like so:

```ts
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
```

We'll discuss index signatures in a bit, but here we're saying a `SquareConfig` can have any number of properties, and as long as they aren't `color` or `width`, their types don't matter.

One final way to get around these checks, which might be a bit surprising, is to assign the object to another variable:
Since `squareOptions` won't undergo excess property checks, the compiler won't give you an error.

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

Keep in mind that for simple code like above, you probably shouldn't be trying to "get around" these checks.
For more complex object literals that have methods and hold state, you might need to keep these techniques in mind, but a majority of excess property errors are actually bugs.
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.
Expand Down Expand Up @@ -218,10 +230,11 @@ mySearch = function(src, sub) {
}
```

# Array Types
# Indexable Types

Similarly to how we can use interfaces to describe function types, we can also describe array types.
Array types have an `index` type that describes the types allowed to index the object, along with the corresponding return type for accessing the index.
Similarly to how we can use interfaces to describe function types, we can also describe types that we can "index into" like `a[10]`, or `ageMap["daniel"]`.
Indexable types have an *index signature* that describes the types we can use to index into the object, along with the corresponding return types when indexing.
Let's take an example:

```ts
interface StringArray {
Expand All @@ -230,14 +243,36 @@ interface StringArray {

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];
```

There are two types of supported index types: string and number.
It is possible to support both types of indexers, with the restriction that the type returned from the numeric indexer must be a subtype of the type returned from the string indexer.
Above, we have a `StringArray` interface that has an index signature.
This index signature states that when a `StringArray` is indexed with a `number`, it will return a `string`.

There are two types of supported index signatures: string and number.
It is possible to support both types of indexers, but the type returned from a numeric indexer must be a subtype of the type returned from the string indexer.
This is because when indexing with a `number`, JavaScript will actually convert that to a `string` before indexing into an object.
That means that indexing with `100` (a `number`) is the same thing as indexing with `"100"` (a `string`), so the two need to be consistent.

```ts
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}

// Error: indexing with a 'string' will sometimes get you a Dog!
interface NotOkay {
[x: number]: Animal;
[x: string]: Dog;
}
```

While string index signatures are a powerful way to describe the "dictionary" pattern, they also enforce that all properties match their return type.
This is because a string index declares that `obj.property` is also available as `obj["property"]`.
In this example, `name`'s type does not match the string index's type, and the type-checker gives an error:
In the following example, `name`'s type does not match the string index's type, and the type-checker gives an error:

```ts
interface NumberDictionary {
Expand Down

0 comments on commit 330c41e

Please sign in to comment.