Advanced Types in TypeScript
Learn about Advanced Types in TypeScript with this detailed tutorial. Includes explanations and examples to help you master advanced type features.
Introduction
TypeScript's advanced types allow you to create powerful type definitions that can help you write more robust and maintainable code. In this tutorial, we will cover some of the most important advanced types and their use cases.
Intersection Types
Intersection types allow you to combine multiple types into one. This can be useful when you want to merge the properties of multiple types.
interface Person {
name: string;
}
interface Employee {
employeeId: number;
}
type PersonEmployee = Person & Employee;
const pe: PersonEmployee = {
name: "John",
employeeId: 1234
};
Explanation
In this example, the PersonEmployee
type is created by combining the Person
and Employee
interfaces using the &
operator. The resulting type has all the properties of both interfaces.
Union Types
Union types allow you to define a variable that can hold multiple types. This can be useful when a value can be one of several types.
function printId(id: number | string) {
console.log(`Your ID is: ${id}`);
}
printId(101); // Your ID is: 101
printId("202"); // Your ID is: 202
Explanation
In this example, the printId
function accepts a parameter that can be either a number
or a string
. The function can handle both types of inputs.
Type Guards
Type guards allow you to narrow down the type of a variable within a conditional block. This can be useful when working with union types.
function isString(x: any): x is string {
return typeof x === "string";
}
function printValue(value: number | string) {
if (isString(value)) {
console.log(`String: ${value}`);
} else {
console.log(`Number: ${value}`);
}
}
printValue("Hello"); // String: Hello
printValue(123); // Number: 123
Explanation
In this example, the isString
function is a type guard that checks if a value is a string. The printValue
function uses this type guard to determine the type of the input and handle it accordingly.
Type Aliases
Type aliases allow you to create new names for existing types. This can be useful for simplifying complex type definitions.
type StringOrNumber = string | number;
function printId(id: StringOrNumber) {
console.log(`Your ID is: ${id}`);
}
printId(101); // Your ID is: 101
printId("202"); // Your ID is: 202
Explanation
In this example, the StringOrNumber
type alias is used to create a shorthand for the union type string | number
. The printId
function uses this type alias to specify its parameter type.
Index Types
Index types allow you to get the type of a property from another type. This can be useful for working with dynamic property names.
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person; // "name" | "age"
function getProperty(obj: T, key: K) {
return obj[key];
}
let person: Person = { name: "John", age: 30 };
let personName = getProperty(person, "name"); // John
Explanation
In this example, the keyof
operator is used to get the keys of the Person
interface. The getProperty
function uses this type to ensure that only valid keys are accessed on the object.
Mapped Types
Mapped types allow you to create new types by transforming properties of an existing type. This can be useful for creating variations of a type.
type Readonly = {
readonly [P in keyof T]: T[P];
};
interface Person {
name: string;
age: number;
}
let readonlyPerson: Readonly = { name: "John", age: 30 };
// readonlyPerson.name = "Doe"; // Error: Cannot assign to 'name' because it is a read-only property.
Explanation
In this example, the Readonly
mapped type makes all properties of the Person
interface read-only. This ensures that the properties cannot be modified after the object is created.
Conditional Types
Conditional types allow you to create types based on a condition. This can be useful for creating flexible and reusable types.
type IsString = T extends string ? true : false;
type A = IsString; // true
type B = IsString; // false
Explanation
In this example, the IsString
conditional type checks if a type is a string. The resulting type is either true
or false
based on the condition.
Utility Types
TypeScript provides several utility types that make it easier to work with advanced types. Some common utility types include Partial
, Required
, Pick
, and Omit
.
Partial
The Partial
utility type makes all properties of a type optional.
interface Person {
name: string;
age: number;
}
let partialPerson: Partial = {};
partialPerson.name = "John"; // OK
partialPerson.age = 30; // OK
Required
The Required
utility type makes all properties of a type required.
interface Person {
name?: string;
age?: number;
}
let requiredPerson: Required = {
name: "John",
age: 30
}; // OK
Pick
The Pick
utility type allows you to create a new type by picking specific properties from an existing type.
interface Person {
name: string;
age: number;
address: string;
}
type PersonName = Pick;
let personName: PersonName = {
name: "John"
}; // OK
Omit
The Omit
utility type allows you to create a new type by omitting specific properties from an existing type.
interface Person {
name: string;
age: number;
address: string;
}
type PersonWithoutAddress = Omit;
let personWithoutAddress: PersonWithoutAddress = {
name: "John",
age: 30
}; // OK
Conclusion
Advanced types in TypeScript provide powerful tools for creating flexible, reusable, and type-safe code. By understanding and using these advanced types, you can write more robust and maintainable applications.