| name | typescript-advanced |
| description | Design, implement, refactor, and debug advanced TypeScript patterns including generics, utility types, mapped types, conditional types, type guards, discriminated unions, and strict mode configuration. Use when writing type-safe code, fixing type errors, or improving type inference. |
| license | MIT |
| metadata | {"version":"1.0.0","audience":"developers","workflow":"frontend-development"} |
TypeScript Advanced Patterns
Expert guidance for writing type-safe TypeScript code using advanced type system features.
What I Do
- Design generic types with constraints, defaults, and inference
- Apply utility types (Partial, Pick, Omit, Record, Required, Readonly)
- Create custom mapped and conditional types
- Implement type guards and discriminated unions
- Configure strict mode and debug type errors
- Augment modules and merge declarations
When to Use Me
Use this skill when you:
- Create generic functions, classes, or type-safe APIs
- Fix, debug, or resolve TypeScript type errors
- Write custom mapped or conditional types
- Add type guards for runtime narrowing
- Configure or troubleshoot strict mode settings
- Avoid
any and improve type safety
Generic Patterns
function loggingIdentity<T extends { length: number }>(arg: T): T {
console.log(arg.length);
return arg;
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
type Container<T = string> = { value: T };
Utility Types
interface User { id: number; name: string; email: string; }
type PartialUser = Partial<User>;
type RequiredUser = Required<PartialUser>;
type ReadonlyUser = Readonly<User>;
type UserPreview = Pick<User, "id" | "name">;
type UserWithoutEmail = Omit<User, "email">;
function fetchUser(id: number): Promise<User> { }
type Params = Parameters<typeof fetchUser>;
type Return = ReturnType<typeof fetchUser>;
= << fetchUser>>;
Type Guards
function process(value: string | number) {
if (typeof value === "string") return value.toUpperCase();
return value.toFixed(2);
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
interface Circle { kind: "circle"; radius: number; }
interface Square { kind: "square"; sideLength: number; }
type Shape = Circle | Square;
function getArea(shape: Shape): number {
switch (shape.kind) {
: . * shape. ** ;
: shape. ** ;
: : = shape; _;
}
}
Mapped Types
type OptionsFlags<T> = { [P in keyof T]: boolean };
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
type Concrete<T> = { [P in keyof T]-?: T[P] };
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
Template Literal Types
Template literal types let you compose string-literal types the same way
you compose template literals at runtime. They shine for typed event names,
CSS-in-TS APIs, and route builders.
type EventName<T extends string> = `on${Capitalize<T>}`;
type ButtonEvents = EventName<"click" | "focus" | "blur">;
type CSSValue = `${number}${"px" | "rem" | "em"}`;
type Resource = "users" | "posts" | "comments";
type Action = "list" | "create" | "edit";
type ApiPath = `/api/${Resource}/${Action}`;
Built-in template-literal helpers (TypeScript 4.1+):
Uppercase<"foo">
Lowercase<"FOO">
Capitalize<"foo">
Uncapitalize<"Foo">
Combine with mapped types to drive string-keyed APIs:
type PropEventListeners<T> = {
[K in keyof T as `${string & K}Changed`]?: (newValue: T[K]) => void;
};
interface Form {
email: string;
age: number;
}
Declaration Merging
TypeScript merges multiple declarations that share a name. The most common
forms are interface merging and module augmentation.
Interface Merging
When two interfaces with the same name are exported from the same module,
TypeScript combines their members into one. Useful for extending a third-
party interface (where extends is not an option):
interface User {
id: number;
name: string;
}
interface User {
email: string;
createdAt: Date;
}
const u: User = {
id: 1,
name: "Ada",
email: "ada@example.com",
createdAt: new Date(),
};
Module Augmentation
To add fields to an existing module's exported types, declare the module
again in a file that's part of the compilation:
declare module "express" {
interface Request {
userId?: string;
}
}
app.use((req, _res, next) => {
console.log(req.userId);
next();
});
Rules:
- Both declarations must be in the same module (file) or the augmenting
file must be loaded by the compiler (ambient
.d.ts or referenced via
include).
- You can only merge interfaces (and namespace-style merges with classes),
not type aliases.
- Conflicting member types in the same interface will error.
Use module augmentation sparingly — over-augmenting makes a type harder
to reason about. Prefer wrapping (e.g., a RequestWithUser type) when
the augmented behavior is local.
Conditional Types
type IsString<T> = T extends string ? true : false;
type Flatten<T> = T extends Array<infer Item> ? Item : T;
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>;
type ToArrayNonDist<T> = [T] extends [unknown] ? T[] : never;
type Result = ToArrayNonDist<string | number>;
Context7 Integration
For up-to-date TypeScript documentation, use Context7 MCP server:
- Query: "TypeScript generics constraints" | Library: /microsoft/typescript
- Query: "TypeScript utility types" | Library: /microsoft/typescript
- Query: "TypeScript conditional types infer" | Library: /microsoft/typescript
Common Errors
"Property does not exist on type" - Use in operator or type guard:
if ("a" in obj) { console.log(obj.a); }
"Type is not assignable to type 'never'" - Missing switch case:
"Object is possibly 'undefined'" - Use optional chaining or type guard:
console.log(user.address?.city);
if (user.address) { console.log(user.address.city); }
Avoiding any:
function processData(data: unknown): string {
if (typeof data === "string") return data.toUpperCase();
throw new Error("Unsupported type");
}
type AnyObject = Record<string, unknown>;
type AnyFunction = (...args: unknown[]) => unknown;
Strict Mode Configuration
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
Modern TypeScript (4.9+)
const config = {
port: 8080,
host: "localhost"
} satisfies Record<string, string | number>;
function createArray<const T extends readonly unknown[]>(items: T): T {
return items;
}
const arr = createArray([1, 2, 3]);
async function processFile() {
using file = await openFile("data.txt");
}
Related Skills
References