Skip to content

Commit a4fca07

Browse files
author
Basarat Ali Syed
committed
index signatures
1 parent ed6fefd commit a4fca07

File tree

2 files changed

+107
-18
lines changed

2 files changed

+107
-18
lines changed

code/types/freshness/index-signatures.ts

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
module a {
2-
let foo:any = {};
2+
let foo: any = {};
33
foo['Hello'] = 'World';
44
console.log(foo['Hello']); // World
55
}
66
module b {
77
class Foo {
8-
constructor(public message: string){};
9-
log(){
8+
constructor(public message: string) { };
9+
log() {
1010
console.log(this.message)
1111
}
1212
}
1313

14-
let foo:any = {};
14+
let foo: any = {};
1515
foo['Hello'] = new Foo('World');
1616
foo['Hello'].log(); // World
1717
}
1818
module c {
1919
let obj = {
20-
toString(){
20+
toString() {
2121
console.log('toString called')
2222
return 'Hello'
2323
}
2424
}
2525

26-
let foo:any = {};
26+
let foo: any = {};
2727
foo[obj] = 'World'; // toString called
2828
console.log(foo[obj]); // toString called, World
2929
console.log(foo['Hello']); // World
@@ -36,12 +36,12 @@ module d {
3636

3737
module e {
3838
let obj = {
39-
toString(){
39+
toString() {
4040
return 'Hello'
4141
}
4242
}
4343

44-
let foo:any = {};
44+
let foo: any = {};
4545

4646
// ERROR: the index signature must be string, number ...
4747
foo[obj] = 'World';
@@ -51,8 +51,8 @@ module e {
5151
}
5252

5353
module f {
54-
let obj = {message:'Hello'}
55-
let foo:any = {};
54+
let obj = { message: 'Hello' }
55+
let foo: any = {};
5656

5757
// ERROR: the index signature must be string, number ...
5858
foo[obj] = 'World';
@@ -67,7 +67,7 @@ module f {
6767
}
6868

6969
module g {
70-
let foo:{ [index:string] : {message: string} } = {};
70+
let foo: { [index: string]: { message: string } } = {};
7171

7272
/**
7373
* Must store stuff that conforms the structure
@@ -90,24 +90,24 @@ module mustConform {
9090

9191
/** Okay */
9292
interface Foo {
93-
[key:string]: number
93+
[key: string]: number
9494
x: number;
9595
y: number;
9696
}
9797
/** Error */
9898
interface Bar {
99-
[key:string]: number
99+
[key: string]: number
100100
x: number;
101101
y: string; // Property `y` must of of type number
102102
}
103103
}
104104

105105
module mustConform2 {
106106
interface Foo {
107-
[key:string]: number
107+
[key: string]: number
108108
x: number;
109109
}
110-
let foo: Foo = {x:1,y:2};
110+
let foo: Foo = { x: 1, y: 2 };
111111
foo['x']; // number
112112
let x = 'x'
113113
foo[x]; // number
@@ -123,3 +123,43 @@ module dual {
123123
length: number;
124124
}
125125
}
126+
127+
module jsland {
128+
interface NestedCSS {
129+
color?: string;
130+
[selector: string]: string | NestedCSS;
131+
}
132+
133+
const example: NestedCSS = {
134+
color: 'red',
135+
'.subclass': {
136+
color: 'blue'
137+
}
138+
}
139+
140+
const failsSilently: NestedCSS = {
141+
colour: 'red', // No error is `colour` is a valid string selector
142+
}
143+
}
144+
145+
module better {
146+
interface NestedCSS {
147+
color?: string;
148+
nest?: {
149+
[selector: string]: NestedCSS;
150+
}
151+
}
152+
153+
const example: NestedCSS = {
154+
color: 'red',
155+
nest: {
156+
'.subclass': {
157+
color: 'blue'
158+
}
159+
}
160+
}
161+
162+
const failsSilently: NestedCSS = {
163+
colour: 'red', // TS Error: unknown property `colour`
164+
}
165+
}

docs/types/index-signatures.md

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ foo['x']; // number
166166
let x = 'x'
167167
foo[x]; // number
168168
```
169-
### Extending `string`
169+
### Using a limited set of string literals
170170

171-
An index signature can require that index strings be members of a "vocabulary" union of literal strings, because such a union extends `string`:
171+
An index signature can require that index strings be members of a union of literal strings e.g.:
172172

173173
```ts
174174
type Index = 'a' | 'b' | 'c'
@@ -178,7 +178,7 @@ const good: FromIndex = {b:1, c:2}
178178

179179
// Type '{ b: number; c: number; d: number; }' is not assignable to type 'FromIndex'.
180180
// Object literal may only specify known properties, and 'd' does not exist in type 'FromIndex'.
181-
const bad: FromIndex = {b:1, c:2, d:3} //
181+
const bad: FromIndex = {b:1, c:2, d:3} //
182182
```
183183
This is often used together with `keyof typeof` to capture vocabulary types, described on the next page.
184184

@@ -204,3 +204,52 @@ interface ArrStr {
204204
length: number;
205205
}
206206
```
207+
208+
### TIP: API consideration when adding index signatures
209+
210+
Quite commonly in the JS community you will see APIs that abuse string indexers. e.g. a common pattern among CSS in JS libraries:
211+
212+
```js
213+
interface NestedCSS {
214+
color?: string;
215+
[selector: string]: string | NestedCSS;
216+
}
217+
218+
const example: NestedCSS = {
219+
color: 'red',
220+
'.subclass': {
221+
color: 'blue'
222+
}
223+
}
224+
```
225+
Try not to mix string indexers with *valid* values this way. E.g. a typo in the padding will remain uncaught:
226+
227+
```js
228+
const failsSilently: NestedCSS = {
229+
colour: 'red', // No error is `colour` is a valid string selector
230+
}
231+
```
232+
233+
Instead seperate out the nesting into its own property e.g. in a name like `nest` (or `children` or `subnodes` etc.):
234+
235+
```js
236+
interface NestedCSS {
237+
color?: string;
238+
nest?: {
239+
[selector: string]: NestedCSS;
240+
}
241+
}
242+
243+
const example: NestedCSS = {
244+
color: 'red',
245+
nest: {
246+
'.subclass': {
247+
color: 'blue'
248+
}
249+
}
250+
}
251+
252+
const failsSilently: NestedCSS = {
253+
colour: 'red', // TS Error: unknown property `colour`
254+
}
255+
```

0 commit comments

Comments
 (0)