In TypeScript, interfaces can also describe indexable types i.e. the types which can be accessed via indexes.
In JavaScript objects can be accessed via index. For example
let x = {a: 1, b: 'blah'};
let n = x['a']; //same as x.a
let str = x['b']; // same as x.b
In above snippet, x has properties with different value types. To force 'keys' to have same types and 'values' to have same types, TypeScript supports interfaces to describe indexable as reusable types.
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.
TypeScript only allows two types for indexes (the keys): string and number.
Example
indexable-type-example.ts//defining reusable indexable type
interface States {
[state: string]: boolean;//indexer
}
let s: States = {'enabled': true, 'maximized':false};
console.log(s);
console.log(s['maximized']); Output{ enabled: true, maximized: false } false
TypeScript array vs positional indexer
Consider an example where we can index into the object by number type:
types-with-number-index.tsinterface States {
[index: number]: boolean;
}
let s: States = [true, false];
console.log(s);
console.log(s[0]);
Output[ true, false ] true
As seen above, TypeScript supports array notation (the square brackets) to initialize positional indexable (arrays created with [] are also objects accessible via positional indexes).
Alternatively, we can use TypeScript arrays in above example:
using-array-example.tslet s: boolean[] = [true, false];
console.log(s);
console.log(s[0]); Output[ true, false ] true
So which one is better? The advantage of using interface to describe the 'number' indexable over using array is probably that the indexable interface, being a reusable type, can also be used for objects (created via {} notation). Also array has other members as well e.g. 'length', 'join()', 'push()', etc. The object created via indexer does not have them unless we also define those member in our interface (we will see an example on that shortly). So if we don't want to expose all array functionality we should use indexable types (with positional indexer).
Supported index signature
As stated before, there are two types of supported index signatures: string and number. Following example attempts to use boolean:
wrong-indexable-type-example.tsinterface boolArray {
[b: boolean]: string;
}
Outputwrong-indexable-type-example.ts(2,6): error TS1023: An index signature parameter type must be 'string' or 'number'.
Indexer with other members
Besides indexers, indexable interfaces allow to add additional properties
When using positional indexer (i.e. indexer of number type) we can define array properties/methods which need to be exposed.
with-members.tsinterface States{
[index: number]: boolean;
length: number;
pop(): boolean;
}
let s: States = [true, false, true];
console.log(s);
console.log(s.length);
console.log(s.pop());
console.log(s); Output[ true, false, true ] 3 true [ true, false ]
When using indexer of string type all properties should return the same type that the indexer returns
with-members2.tsinterface States {
[state: string]: boolean;
mainScreen: boolean;
//following not allowed
//screenName: String;
}
let s: States = {mainScreen: false};
s["enabled"] = true;
s["maximized"] = false;
console.log(s);
Output{ mainScreen: false, enabled: true, maximized: false }
If the indexer return type is 'any' then properties can return any types:
with-members3.tsinterface States {
[state: string]: any;
screenName: String;
}
let s: States = {screenName:"main"};
s["enabled"] = true;
s["maximized"] = false;
s["width"]=300;
s["height"]=200;
console.log(s);
Output{ screenName: 'main', enabled: true, maximized: false, width: 300, height: 200 }
Example ProjectDependencies and Technologies Used: |