Interfaces
Type compatibility with object varibale
The compiler only checks that at least the ones required are present and match the types required when using object variables. It means that you should have all properties inside declaration, and still can have properties more than declaration.
1 | interface LabeledValue { |
Excess property check with object literal
When it comes to object literals, excess properties are not allowed. It means that you can’t have properties more than declaration.
1 | interface LabeledValue { |
However, there are some workarounds.
1 | // Add a type assertion |
* Detailed referrence.
Optional properties
Not all properties of an interface may be required, they are possibly available.
1 | interface Config { |
Readonly properties
This type of properties could only be modifiable when first created.
1 | interface Point { |
There exists readonly array, which has all mutating methods removed.
1 | let arr: number[] = [1, 2, 3, 4]; |
* readonly vs const
The easiest way to remember whether to use readonly
or const
is to ask whether you’re using it on a variable or a property. Variables use const
whereas properties use readonly
.
Function types
Names of parameters do not need to match. Types are allowed to not specified, with typescript’s contextual typing which can infer the argument types.
1 | interface SearchFunc { |
Indexable Types
Take an example as below. 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
.
1 | interface StringArray { |
There are two types of supported index signatures: string and number. But there is a rule: the type returned from a numeric indexer must be a subtype of the type returned from a string indexer. Because numeric index will actually be converted into string index.
1 | interface AType { |
Another note is that, string index signatures enforce that all properties match their return type. Because obj.property
is also available as obj["property]
.
1 | interface Dictionary { |
However, properties of different types are acceptable if the index signature is a union of the property types.
1 | interface Dictionary { |
What’s more, make index signatures readonly
can prevent indeces from being assigned. (Though I’m a little bit confused…)
1 | interface ReadonlyStringArray { |
Class types
Nothing special when it goes without constructor constrained.
1 | interface ClockInterface { |
It’s different when it comes to having a construct signature. Below would get an error.
1 | interface ClockConstructor { |
Firstly, notice that class has two types: the type of the static side and the type of the instance side.
Secondly, when a class implements an interface, only the instance side of the class is checked, while the constructor sits in the static side and is not included in the check.
So, we need to work with the static side of the class directly.
I think it might take some time to understand example below. This time, createClock
‘ s frist parameter should be of type ClockConstructor
, and DigitalClock
/AnalogClock
which is passed as param would get checked.
1 | // For the constructor in the static side |
Another way is to use class expressions:
1 | ... |
Extending interfaces
Interfaces can extend each other, even can extend multiple interfaces.
1 | interface Shape { |
Hybrid types
Just a combination of above. For example, below is an object that acts as both function and object.
1 | interface Counter { |
Interfaces extending classes
Several key points:
- interfaces would have all of the class’ members but not their implementations
- members include the private and protected ones, but the interface thus could only be implements by that class or a subclass of it
1 | // The class |