A deep dive into TypeScript interfaces and how types are linked.
Interfaces are an abstract type that enable you to define properties within a given object. Looking at the properity names and data type for each one.
In a React application, you might use interfaces to define the type of props used within the application.
Yes, you must define the type of data within the interface.
You can use eithe type or interface when building interfaces. See below.
interface Person {
name: string;
age: number;
}
function greeting(person: Person) {
return "Hello " + person.name + " you are " + person.age + " years old.";
}
Is the same asβ¦
type Person = {
name: string;
age: number;
};
function greeting(person: Person) {
return "Hello " + person.name + " you are " + person.age + " years old.";
}
Optional properties use a ? to signify they are optional.
interface CoOrds {
xPos?: number;
yPos?: number;
}
function getCorOrds(dest: CoOrds) {
return CoOrds.xPos + "," + CoOrds.xPos;
}
getCoOrds({ xPos: 100, yPos: 100 });
getCoOrds({ xPos: 100 });
getCoOrds({ yPos: 100 });
Behaviour wonβt change at runtime however if a property is marked as readonly it canβt be written to during type checking.
interface Example {
readonly text: string;
}
function readOnlyExample(obj: Example) {
return obj.text;
}
Note: text cannot be re-assigned in the function
Using readonly doesnβt always imply the value is immutable, it just means the property itself cannot be rewritten to.
interface Person {
name: string;
age: number;
}
interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}
let writablePerson: Person = {
name: "Person A",
age: 100,
};
let readonlyPerson: ReadonlyPerson = writeablePerson;
console.log(readonlyPerson.age); // 100
writeablePerson.age++;
console.log(readonlyPerson.age); // 101
Using mapping modifiers, you can remove readonly attributes.
function Example(value: Array<string>) {
return console.log("example");
}
let myArr: string[] = ["one", "two"];
Example(myArr);
Example(new Array("one", "two"));
Array itself is also a generic type.
interface Array<Type> {
length: number;
pop(): Type | undefined;
push(...items: Type[]): number;
}
Modern JavaScript also provides other data structures which are generic, like Map<K, V>, Set<T>, and Promise<T>. All this really means is that because of how Map, Set, and Promise behave, they can work with any sets of types.
This type describes arrays that shouldnβt be changed.
const myArr: ReadonlyArray<string> = ["one", "two", "three"];
You canβt do the following:
let x: readonly string[] = [];
let y: string[] = [];
x = y;
x = x;
Unlike the readonly property modifier, assignability isnβt bidirectional between Arrays and ReadonlyArrays.
Another sort of Array type that knows exactly how many elements it contains and exactly which types it contains at specific positions.
type StrNumPair = [string, number];
function Example(pair: [string, number]) {
const a = pair[0];
const b = pair[1];
}
Example(["hello", 100]);
function Example(stringHash: [string, number]) {
const [inputStr, hash] = stringHash;
console.log(inputStr);
console.log(hash);
}
interface StringNumber {
length: 2;
0: string;
1: number;
slice(start?: number, end?: number): Array<string | number>;
}
function Example(pair: readonly [string, number]) {
return pair;
}
Sometimes you donβt know all the names of a typeβs properties ahead of time. In those cases you can use an index signature to describe the types of the possible values.
interface StringArr {
[index: number]: string;
}
const myArr: StringArr = getStringArr();
const secondItem = myArr[1];
The following code block wouldnβt work:
interface Example {
[index: string]: number;
length: number;
name: string; // name cannot be a string as index only has type number
}
However the following would work:
interface ExampleFixed {
[index: string]: number | string;
length: number;
name: string;
}
You can make index signatures readonly in order to prevent assigment to indices.
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArr: ReadonlyStringArray = getReadOnlyStringArr();
myArr[2] = "Example";
The extends keyword on interface allows you to copy members from other named types and add new properties. This can be useful for cutting down declaration boilerplate.
interface BasicAddress {
name?: string;
street: string;
city: string;
country: string;
postCode: string;
}
interface AddressWithUnit {
name?: string;
unit: string;
street: string;
city: string;
country: string;
postCode: string;
}
interface AddressWithUnit extends BasicAddress {
unit: string;
}
interface SelectColour {
colour: string;
}
interface Circle {
radius: number;
}
interface SelectColour extends Circle {}
const cc: ColourfulCircle = {
clour: "green",
radius: 3.14,
};
Interfaces allow you to build new types and extend types from other types. TypeScript provides intersection types and is mainly used to combine existing object types.
Intersection type is defined by &.
interface SelectColour {
color: string;
}
interface Circle {
radius: number;
}
type SelectColour = SelectColour & Circle;