Advanced Types¶
Intersection types¶
-
combine multiple types into one type.
type T1 = { a: string }; type T2 = { b: string }; type T3 = T1 & T2; const t3: T3 = { a: "a", b: "b" };
type Combinable = string | number; type Numeric = number | boolean; type Universal = Combinable & Numeric; // string | number | boolean
Type guards¶
- type guards are used to check if a value is of a certain type.
- type guards using
typeof
operator - type guards using
in
operator - type guards using
instanceof
operator (only works with classes) -
type guards using discriminated unions: specify a unique type for each branch of the union, then use a type guard to check if the value is of that type.
-
example:
type StringOrNumber = string | number;
function add(a: StringOrNumber, b: StringOrNumber) {
if (typeof a === "string" || typeof b === "string") {
// typeof operator
return a.toString() + b.toString();
}
return a + b;
}
type T1 = { a: string };
type T2 = { b: string };
type T3 = T1 | T2;
function printType(t: T3) {
if ("a" in t) {
// in operator
console.log("T1");
} else if ("b" in t) {
console.log("T2");
}
}
class Person {
name: string;
}
class Human {
name: string;
}
function printType(p: Person) {
if (p instanceof Person) {
// instanceof operator
console.log("Person");
} else if (p instanceof Human) {
console.log("Human");
}
}
- example, discriminated unions:
type A = {
a: string;
type: "A"; // discriminant property, only used to identify the type
};
type B = {
b: string;
type: "B"; // discriminant property
};
type C = A | B;
function f(c: C) {
if (c.type === "A") {
// discriminated union
console.log(c.a);
} else if (c.type === "B") {
console.log(c.b);
}
}
Type casting¶
- we can cast a value to a different type.
- useful to override a generic type to a more specific type.
const input = document.getElementById("input"); // HTMLElement | null
const input2 = document.getElementById("input")!; //HTMLElement
const input3 = <HTMLInputElement>input; // HTMLInputElement (type casting option 1), not suitable fo JSX
const input4 = input as HTMLInputElement; // HTMLInputElement (type casting option 2)
Index properties¶
- index properties are used to specify the type of object’s properties without knowing the actual keys.
- if you used index properties, you can use the
in
operator to check if a property exists. - if you used index properties, you can use the
keyof
operator to get the keys of an object. - if you used index properties, you can use the
typeof
operator to get the type of a property. - if you used index properties, you can not have actual keys from a different type.
type Person = {
name: string; // actual property
[key: string]: string; // index properties
age: number; // error, age is not a string
};
Function overloads¶
- function overloads are used to specify different implementations of the same function.
type StringOrNumber = string | number;
function add(a: number, b: number): number; // function signature 1, if a and b are both numbers, return a number
function add(a: string, b: string): string; // function signature 2, if a and b are both strings, return a string
function add(a: StringOrNumber, b: StringOrNumber) {
// actual function implementation
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
// without overloads
const x = add(1, 2); // 3, StringOrNumber
x.toString(); // error, toString does not exist on StringOrNumber
// with overloads
const x = (1, 2); // 3, number
const y = add("a", "b"); // "ab", string
Optional chaining¶
- optional chaining is used to access properties of an object that may or may not exist.
- this get compiled into javascript code as
if statement
that checks if the object exists and then accesses the property. obj?.nestedObj?.property
is the same asobj?.nestedObj?.property ?? undefined
obj?.nestedObj
is the same asobj?.nestedObj || undefined
Nullish coalescing¶
- nullish coalescing is used to access properties of an object that may or may not exist.
- uses
??
operator. - works only if the left side is not null or undefined.
obj?.nestedObj?.property ?? defaultValue
, defaultValue will be resolved if either nestedObj or nestedObj.property is null or undefined.