Part 3: Advanced TypeScript Concepts
- Published on
- Authors
- Name
- Bobby Hall Jr
- @bobbyhalljr_dev
Section 3: Advanced TypeScript Concepts
In this section, we will explore advanced concepts in TypeScript that allow us to write more expressive and robust code. We will cover union and intersection types, type inference, generics, and type guards.
Union and Intersection Types
Union types allow us to specify that a value can be of multiple types. We use the |
operator to denote a union type.
function printId(id: number | string): void {
console.log(`ID: ${id}`)
}
printId(123) // Valid
printId('abc') // Valid
printId(true) // Error: Argument of type 'boolean' is not assignable to parameter of type 'number | string'
Intersection types allow us to combine multiple types into a single type. We use the &
operator to denote an intersection type.
interface Printable {
print: () => void
}
interface Loggable {
log: () => void
}
function createLogger(): Printable & Loggable {
return {
print: () => console.log('Printing...'),
log: () => console.log('Logging...'),
}
}
const logger = createLogger()
logger.print() // Valid
logger.log() // Valid
logger.sayHello() // Error: Property 'sayHello' does not exist on type 'Printable & Loggable'
Type Inference
TypeScript's type inference system allows the compiler to deduce the types of variables and expressions based on their context and usage. This reduces the need for explicit type annotations in many cases.
let message = 'Hello, TypeScript!' // TypeScript infers the type as string
let count = 5 // TypeScript infers the type as number
function add(a: number, b: number) {
return a + b // TypeScript infers the return type as number
}
const person = {
name: 'John Doe',
age: 30,
} // TypeScript infers the type as { name: string, age: number }
Type inference enhances code readability and maintainability while still providing type safety.
Generics
Generics in TypeScript allow us to create reusable components that can work with a variety of types. They enable us to write type-safe code while maintaining flexibility.
function toArray<T>(arg: T): T[] {
return [arg]
}
const numbers = toArray(1) // numbers inferred as number[]
const names = toArray('John', 'Jane') // names inferred as string[]
const values = toArray(1, 'two') // Error: Argument of type 'string' is not assignable to parameter of type 'number'
Generics provide a way to abstract over types and create more flexible and reusable code.
Type Guards
Type guards allow us to narrow down the type of a variable within a conditional block, based on a runtime check. This enables us to write more precise and type-safe code.
function logLength(value: string | string[]): void {
if (typeof value === 'string') {
console.log(value.length) // Valid: value is narrowed down to string
} else {
console.log(value.length) // Valid: value is narrowed down to string[]
}
}
Type guards help us write code that handles different types correctly and prevents runtime errors.
In the next section, we will learn about modules and imports in TypeScript, which allow us to organize and modularize our codebase.