In this section, we will delve into advanced techniques for manipulating types in TypeScript. These techniques are essential for creating more flexible and reusable code, especially in large-scale applications. We will cover the following topics:
- Mapped Types
- Conditional Types
- Template Literal Types
- Key Remapping in Mapped Types
- Recursive Types
- Mapped Types
Mapped types allow you to create new types by transforming properties of an existing type. They are particularly useful for creating variations of a type.
Example: Readonly Type
type Readonly<T> = { readonly [P in keyof T]: T[P]; }; interface User { id: number; name: string; } const user: Readonly<User> = { id: 1, name: "Alice" }; // user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.
Explanation
Readonly<T>
is a mapped type that takes a typeT
and makes all its properties read-only.[P in keyof T]
iterates over each propertyP
in the typeT
.readonly
makes each property read-only.
- Conditional Types
Conditional types allow you to create types based on a condition. They are written using the extends
keyword.
Example: Extracting Types
type ExtractString<T> = T extends string ? T : never; type A = ExtractString<string>; // string type B = ExtractString<number>; // never
Explanation
ExtractString<T>
checks ifT
extendsstring
.- If
T
is a string, it returnsT
; otherwise, it returnsnever
.
- Template Literal Types
Template literal types allow you to create new string literal types by combining string literals.
Example: Creating URL Types
type Protocol = "http" | "https"; type Domain = "example.com" | "example.org"; type URL = `${Protocol}://${Domain}`; const url1: URL = "http://example.com"; // Valid const url2: URL = "ftp://example.com"; // Error: Type '"ftp://example.com"' is not assignable to type 'URL'.
Explanation
${Protocol}://${Domain}
creates a new type by combiningProtocol
andDomain
using template literals.
- Key Remapping in Mapped Types
Key remapping allows you to transform the keys of a type in a mapped type.
Example: Prefixing Keys
type PrefixKeys<T, Prefix extends string> = { [K in keyof T as `${Prefix}${K & string}`]: T[K]; }; interface User { id: number; name: string; } type PrefixedUser = PrefixKeys<User, "user_">; // Resulting type: // type PrefixedUser = { // user_id: number; // user_name: string; // }
Explanation
[K in keyof T as
${Prefix}${K & string}]
remaps the keys ofT
by prefixing them withPrefix
.
- Recursive Types
Recursive types are types that reference themselves. They are useful for representing nested structures.
Example: JSON Type
type JSONValue = | string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }; const json: JSONValue = { name: "Alice", age: 30, isAdmin: true, friends: ["Bob", "Charlie"], address: { city: "Wonderland", zip: "12345" } };
Explanation
JSONValue
is a recursive type that can be a string, number, boolean, null, an array ofJSONValue
, or an object withJSONValue
properties.
Practical Exercise
Exercise: Create a Deep Readonly Type
Create a type DeepReadonly<T>
that makes all properties of T
and its nested objects read-only.
type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]; }; interface User { id: number; name: string; address: { city: string; zip: string; }; } const user: DeepReadonly<User> = { id: 1, name: "Alice", address: { city: "Wonderland", zip: "12345" } }; // user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property. // user.address.city = "New City"; // Error: Cannot assign to 'city' because it is a read-only property.
Solution Explanation
DeepReadonly<T>
makes all properties ofT
read-only.- If a property is an object, it recursively applies
DeepReadonly
to that property.
Conclusion
In this section, we explored advanced type manipulation techniques in TypeScript, including mapped types, conditional types, template literal types, key remapping in mapped types, and recursive types. These techniques are powerful tools for creating flexible and reusable types, especially in complex applications. Understanding and mastering these concepts will significantly enhance your TypeScript skills and enable you to write more robust and maintainable code.
TypeScript Course
Module 1: Introduction to TypeScript
- What is TypeScript?
- Setting Up the TypeScript Environment
- Basic Types
- Type Annotations
- Compiling TypeScript
Module 2: Working with Types
Module 3: Advanced Types
Module 4: Functions and Modules
Module 5: Asynchronous Programming
Module 6: Tooling and Best Practices
- Linting and Formatting
- Testing TypeScript Code
- TypeScript with Webpack
- TypeScript with React
- Best Practices