The function of the interface is to declare the structure and method of variables, but not to make concrete implementation. Typically, interfaces force type checks on all members, including number and type:
interface Name { first: string; second: string; } let name: Name; name = { first: 'John', second: 'Doe' }; name = { // Error: 'second is missing' first: 'John' }; name = { // Error: 'second is the wrong type' first: 'John', second: 1337 };
optional attribute
Interfaces with optional attributes are similar to ordinary interface definitions, except that a? Symbol is added after the name definition of optional attributes:
interface Name { first?: string; second?: string; } let name: Name; name = { // Only first will not report errors first: 'John' };
Read-only property
Some object attributes can only modify the value of an object when it is just created. readonly can be used to specify read-only properties before the property name:
interface Point { readonly x: number; readonly y: number; } let p1: Point = { x: 10, y: 20 }; p1.x = 5; // error!
TypeScript has the ReadonlyArray < T > type, which is similar to Array < T > except that all the variable methods are removed, thus ensuring that the array can never be modified after it is created:
let a: number[] = [1, 2, 3, 4]; let ro: ReadonlyArray<number> = a; ro[0] = 12; // error! ro.push(5); // error! ro.length = 100; // error! a = ro; // error!
readonly is for an object's attributes and const is for a variable. The two usage scenarios are different.
Additional property checking
Literally, it means checking the undefined properties of the interface.
When a variable implements an interface and there are undefined properties in the variable, TypeScript will make an error:
interface Point { x: number; y?: number; } // error! const myPoint: Point = { x: 1, z: 3 };
The easiest way to bypass the check is to use type assertions:
const myPoint: Point = { x: 1, z: 3 } as Point;
The best way is to add a string index signature when defining the interface:
interface Point { x: number; y?: number; [propName: string]: any; }
Function type
Interfaces can be used to represent function types, and an invocation signature needs to be defined for the interface:
interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(source: string, subString: string) { const result = source.indexOf(subString); return result > -1; };
Unlike interfaces of variable types, the parameter names of functions do not need to match those of interface definitions. The parameters of the function are checked one by one, requiring that the parameter types at the corresponding positions are compatible:
let mySearch: SearchFunc; mySearch = function(src: string, sub: string): boolean { let result = src.indexOf(sub); return result > -1; };
If no type is specified, the type system of TypeScript infers the parameter type because the function is assigned directly to the SearchFunc type variable:
let mySearch: SearchFunc; mySearch = function(src, sub) { let result = src.indexOf(sub); return result > -1; };
Indexable types
The indexable type has an index signature that describes the type of object index and the corresponding index return value type.
interface StringArray { [index: number]: string; } let myArray: StringArray; myArray = ['Bob', 'Fred']; let myStr: string = myArray[0];
The example above defines the StringArray interface, which has an index signature. This index signature represents the return value of string type when StringArray is indexed with number.
TypeScript supports two kinds of index signatures: strings and numbers. Two types of indexes can be used at the same time, but the return value of a digital index must be a subtype of the return value type of a string index. Actually, this is the key of a JavaScript object, and the numeric type is converted to a string type and then to a value.
String indexed signatures can describe dictionary patterns well and ensure that all attributes match their return value types:
interface NumberDictionary { [index: string]: number; length: number; // Yes, length is number type name: string; // Error, the type of name does not match the type of index type return value }
The index signature can be set to read-only, thus preventing index assignment:
interface ReadonlyStringArray { readonly [index: number]: string; } let myArray: ReadonlyStringArray = ['Alice', 'Bob']; myArray[2] = 'Mallory'; // error!
Class type
Class can implement an interface using the implements keyword:
interface Point { x: number; y?: number; getX(): number; } class MyPoint implements Point { constructor(x: number) { this.x = x; } x: number; getX(): number { return this.x; } }
Interfaces describe the public part of a class, not the public and private parts. It doesn't help you check whether a class has some private members.
Inheritance interface
Interfaces can inherit from each other, and an interface can inherit multiple interfaces, that is, to copy members of one interface into another interface:
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } let square = <Square>{}; square.color = 'blue'; square.sideLength = 10; square.penWidth = 5.0;
Mixed type
An object can have multiple types at the same time. An example is that an object can be used as both a function and an object with additional attributes:
interface Counter { (start: number): string; interval: number; reset(): void; } function getCounter(): Counter { let counter = <Counter>function(start: number) {}; counter.interval = 123; counter.reset = function() {}; return counter; } let c = getCounter(); c(10); c.reset(); c.interval = 5.0;