Type Aliases
1. Welcome and Icebreaker:
- Welcome! If you could create an alias for any complex concept in real life to make it simpler to refer to, what would it be?
2. Outline the Session:
- Today, we’ll learn about Type Aliases in TypeScript.
- We’ll cover: what they are, how to define them for primitive types, union types, object types, and even generic types.
- Learning Outcomes: You’ll be able to create custom names for types to improve code readability and reusability.
- Let’s simplify some types!
3. Concept Explanation:
-
Type Aliases allow you to create a new name (an alias) for an existing type. They don’t create new types; they simply provide a different name to refer to a type.
-
This is particularly useful for complex types like unions, intersections, tuples, or complex object structures, making your code cleaner and easier to understand.
-
The keyword
typeis used to define a type alias. -
Syntax:
type AliasName = ExistingType; -
Use Cases:
- Simplifying Complex Types: Make long union types or object types more manageable.
type StringOrNumber = string | number;type Point = { x: number; y: number }; - Improving Readability: Give semantic meaning to a type.
type UserID = string;type ProductCode = number; - Reusable Types: Define a complex type once and reuse it in multiple places.
- Generic Type Aliases: Type aliases can also be generic.
type Nullable<T> = T | null | undefined;
- Simplifying Complex Types: Make long union types or object types more manageable.
-
Type Aliases vs. Interfaces:
- Both can be used to describe the shape of an object.
- Key Difference: Interfaces can be implemented by classes and can be extended by other interfaces. Type aliases are more flexible and can represent unions, intersections, primitives, and more complex mapped types.
- Declaration Merging: Interfaces support declaration merging (if you define an interface with the same name multiple times, TypeScript merges their definitions). Type aliases do not.
- General Guideline: Use
interfacefor defining the shape of objects or when you need features likeimplementsorextends. Usetypefor unions, intersections, primitives, or more complex type manipulations.
4. Live Coding Demonstration:
console.log("--- Type Aliases for Primitives ---");
type MyString = string;
type MyNumber = number;
let myName: MyString = "Alice";
let myAge: MyNumber = 30;
console.log(`Name: ${myName}, Age: ${myAge}`);
console.log("\n--- Type Aliases for Union Types ---");
type StringOrNumber = string | number;
let identifier: StringOrNumber = "ID123";
console.log(`Identifier (string): ${identifier}`);
identifier = 456;
console.log(`Identifier (number): ${identifier}`);
// identifier = true; // Error: Type 'boolean' is not assignable to type 'StringOrNumber'.
console.log("\n--- Type Aliases for Object Types ---");
type Point = {
x: number;
y: number;
};
let p1: Point = { x: 10, y: 20 };
console.log(`Point p1: x=${p1.x}, y=${p1.y}`);
// Can also be used for function signatures
type MathOperation = (a: number, b: number) => number;
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
console.log(`Add 5 and 3: ${add(5, 3)}`);
console.log(`Subtract 5 from 3: ${subtract(3, 5)}`);
console.log("\n--- Type Aliases for Tuples ---");
type StringNumberPair = [string, number];
let userTuple: StringNumberPair = ["admin", 1];
console.log(`User Tuple: ${userTuple[0]}, ${userTuple[1]}`);
console.log("\n--- Reusable Type Aliases ---");
type User = {
id: StringOrNumber; // Using another type alias
name: string;
isActive: boolean;
location?: Point; // Optional property using another type alias
};
let user1: User = {
id: "u101",
name: "Bob",
isActive: true,
location: { x: 5, y: 15 }
};
let user2: User = {
id: 202,
name: "Charlie",
isActive: false
};
function printUser(user: User): void {
console.log(`User ID: ${user.id}, Name: ${user.name}, Active: ${user.isActive}`);
if (user.location) {
console.log(` Location: x=${user.location.x}, y=${user.location.y}`);
}
}
printUser(user1);
printUser(user2);
console.log("\n--- Generic Type Aliases ---");
type Container<T> = {
value: T;
};
let numberContainer: Container<number> = { value: 123 };
let stringContainer: Container<string> = { value: "Hello Generics" };
let userContainer: Container<User> = { value: user1 };
console.log(`Number Container:`, numberContainer.value);
console.log(`String Container:`, stringContainer.value);
console.log(`User Container Name:`, userContainer.value.name);
type Nullable<T> = T | null;
let nullableString: Nullable<string> = "I am a string";
console.log(`Nullable String: ${nullableString}`);
nullableString = null;
console.log(`Nullable String (now null): ${nullableString}`);
// nullableString = undefined; // Error if strictNullChecks is on and undefined is not part of Nullable
// Example: Type alias for a function that takes a generic container
type ProcessContainer<T> = (container: Container<T>) => void;
const processNumberContainer: ProcessContainer<number> = (container) => {
console.log(`Processing number: ${container.value * 2}`);
};
processNumberContainer(numberContainer);
5. Tasks:
- Short Exercise:
- Primitive Alias: Create a type alias
Agefor thenumbertype. Declare a variable of typeAge. - Union Alias: Create a type alias
StatusCodethat can represent HTTP status codes like200,404, or500(use a union of number literals). - Object Alias: Create a type alias
Productfor an object with properties:id(string or number),name(string),price(number), and an optionaldescription(string). - Function Signature Alias: Create a type alias
Loggerfor a function that takes astringmessage and returnsvoid. - Generic Alias: Create a generic type alias
Maybe<T>that represents a value of typeTornullorundefined. - Using Aliases: Declare variables for each of the aliases you created and initialize them with appropriate values. For the
ProductandLogger, also try to use them (e.g., create a product object, create a logger function).
- Primitive Alias: Create a type alias