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
typeofoperator - type guards using
inoperator - type guards using
instanceofoperator (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
inoperator to check if a property exists. - if you used index properties, you can use the
keyofoperator to get the keys of an object. - if you used index properties, you can use the
typeofoperator 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 statementthat checks if the object exists and then accesses the property. obj?.nestedObj?.propertyis the same asobj?.nestedObj?.property ?? undefinedobj?.nestedObjis 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.