Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
.claude/skills/wshobson-typescript-advanced-types/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 105% | 0% |
| case-01 | ✓→✗ | ▼ Worse | 171% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 312% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 230% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 181% | 0% |
Comprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.
Purpose: Create reusable, type-flexible components while maintaining type safety.
Basic Generic Function:
typescriptfunction identity<T>(value: T): T { return value; } const num = identity<number>(42); // Type: number const str = identity<string>("hello"); // Type: string const auto = identity(true); // Type inferred: boolean
Generic Constraints:
typescriptinterface HasLength { length: number; } function logLength<T extends HasLength>(item: T): T { console.log(item.length); return item; } logLength("hello"); // OK: string has length logLength([1, 2, 3]); // OK: array has length logLength({ length: 10 }); // OK: object has length // logLength(42); // Error: number has no length
Multiple Type Parameters:
typescriptfunction merge<T, U>(obj1: T, obj2: U): T & U { return { ...obj1, ...obj2 }; } const merged = merge({ name: "John" }, { age: 30 }); // Type: { name: string } & { age: number }
Purpose: Create types that depend on conditions, enabling sophisticated type logic.
Basic Conditional Type:
typescripttype IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false
Extracting Return Types:
typescripttype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never; function getUser() { return { id: 1, name: "John" }; } type User = ReturnType<typeof getUser>; // Type: { id: number; name: string; }
Distributive Conditional Types:
typescripttype ToArray<T> = T extends any ? T[] : never; type StrOrNumArray = ToArray<string | number>; // Type: string[] | number[]
Nested Conditions:
typescripttype TypeName<T> = T extends string ? "string" : T extends number ? "number" : T extends boolean ? "boolean" : T extends undefined ? "undefined" : T extends Function ? "function" : "object"; type T1 = TypeName<string>; // "string" type T2 = TypeName<() => void>; // "function"
Purpose: Transform existing types by iterating over their properties.
Basic Mapped Type:
typescripttype Readonly<T> = { readonly [P in keyof T]: T[P]; }; interface User { id: number; name: string; } type ReadonlyUser = Readonly<User>; // Type: { readonly id: number; readonly name: string; }
Optional Properties:
typescripttype Partial<T> = { [P in keyof T]?: T[P]; }; type PartialUser = Partial<User>; // Type: { id?: number; name?: string; }
Key Remapping:
typescripttype Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]; }; interface Person { name: string; age: number; } type PersonGetters = Getters<Person>; // Type: { getName: () => string; getAge: () => number; }
Filtering Properties:
typescripttype PickByType<T, U> = { [K in keyof T as T[K] extends U ? K : never]: T[K]; }; interface Mixed { id: number; name: string; age: number; active: boolean; } type OnlyNumbers = PickByType<Mixed, number>; // Type: { id: number; age: number; }
Purpose: Create string-based types with pattern matching and transformation.
Basic Template Literal:
typescripttype EventName = "click" | "focus" | "blur"; type EventHandler = `on${Capitalize<EventName>}`; // Type: "onClick" | "onFocus" | "onBlur"
String Manipulation:
typescripttype UppercaseGreeting = Uppercase<"hello">; // "HELLO" type LowercaseGreeting = Lowercase<"HELLO">; // "hello" type CapitalizedName = Capitalize<"john">; // "John" type UncapitalizedName = Uncapitalize<"John">; // "john"
Path Building:
typescripttype Path<T> = T extends object ? { [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never; }[keyof T] : never; interface Config { server: { host: string; port: number; }; database: { url: string; }; } type ConfigPath = Path<Config>; // Type: "server" | "database" | "server.host" | "server.port" | "database.url"
Built-in Utility Types:
typescript// Partial<T> - Make all properties optional type PartialUser = Partial<User>; // Required<T> - Make all properties required type RequiredUser = Required<PartialUser>; // Readonly<T> - Make all properties readonly type ReadonlyUser = Readonly<User>; // Pick<T, K> - Select specific properties type UserName = Pick<User, "name" | "email">; // Omit<T, K> - Remove specific properties type UserWithoutPassword = Omit<User, "password">; // Exclude<T, U> - Exclude types from union type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c" // Extract<T, U> - Extract types from union type T2 = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b" // NonNullable<T> - Exclude null and undefined type T3 = NonNullable<string | null | undefined>; // string // Record<K, T> - Create object type with keys K and values T type PageInfo = Record<"home" | "about", { title: string }>;
Detailed sections (starting with ## Advanced Patterns) live in references/details.md. Read that file when the navigation summary above is insufficient.
unknown over any: Enforce type checkinginterface for object shapes: Better error messagestype for unions and complex types: More flexibletypescript// Type assertion tests type AssertEqual<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false; type Test1 = AssertEqual<string, string>; // true type Test2 = AssertEqual<string, number>; // false type Test3 = AssertEqual<string | number, string>; // false // Expect error helper type ExpectError<T extends never> = T; // Example usage type ShouldError = ExpectError<AssertEqual<string, number>>;
any: Defeats the purpose of TypeScript| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→fail | 6,029 | 5,260 | -13% | 1 | 1 | 0% | 1,165 | 3,162 | +171% | 0 | 0 | — |
case-02 | pass→pass | 3,733 | 5,662 | +52% | 1 | 1 | 0% | 800 | 3,295 | +312% | 0 | 0 | — |
case-03 | fail→fail | 5,729 | 8,410 | +47% | 1 | 1 | 0% | 1,210 | 3,958 | +227% | 0 | 0 | — |
case-04 | fail→pass | 68,768 | 6,082 | -91% | 1 | 1 | 0% | 1,676 | 3,444 | +105% | 0 | 0 | — |
case-05 | pass→pass | 4,627 | 5,246 | +13% | 1 | 1 | 0% | 994 | 3,284 | +230% | 0 | 0 | — |
case-06 | pass→pass | 4,958 | 4,642 | -6% | 1 | 1 | 0% | 1,067 | 3,002 | +181% | 0 | 0 | — |
case-07 | pass→pass | 72,005 | 11,778 | -84% | 1 | 1 | 0% | 2,538 | 4,510 | +78% | 0 | 0 | — |
case-08 | fail→fail | 11,093 | 9,469 | -15% | 1 | 1 | 0% | 2,276 | 4,030 | +77% | 0 | 0 | — |
case-09 | pass→pass | 4,708 | 5,532 | +18% | 1 | 1 | 0% | 1,035 | 3,171 | +206% | 0 | 0 | — |
case-10 | pass→pass | 7,441 | 6,023 | -19% | 1 | 1 | 0% | 1,596 | 3,458 | +117% | 0 | 0 | — |
case-11 | pass→pass | 6,676 | 5,696 | -15% | 1 | 1 | 0% | 1,291 | 3,115 | +141% | 0 | 0 | — |
case-12 | pass→pass | 11,558 | 12,212 | +6% | 1 | 1 | 0% | 2,463 | 4,750 | +93% | 0 | 0 | — |
case-13 | pass→pass | 11,465 | 10,666 | -7% | 1 | 1 | 0% | 2,452 | 4,543 | +85% | 0 | 0 | — |
case-14 | pass→pass | 3,786 | 3,531 | -7% | 1 | 1 | 0% | 855 | 2,914 | +241% | 0 | 0 | — |
case-15 | pass→pass | 18,160 | 12,441 | -31% | 1 | 1 | 0% | 4,031 | 4,796 | +19% | 0 | 0 | — |
case-16 | pass→pass | 15,313 | 9,589 | -37% | 1 | 1 | 0% | 2,743 | 4,143 | +51% | 0 | 0 | — |
case-17 | fail→fail | 7,946 | 6,004 | -24% | 1 | 1 | 0% | 1,744 | 3,391 | +94% | 0 | 0 | — |
case-18 | pass→pass | 3,215 | 4,156 | +29% | 1 | 1 | 0% | 725 | 3,083 | +325% | 0 | 0 | — |
case-19 | fail→fail | 9,898 | 9,837 | -1% | 1 | 1 | 0% | 2,110 | 4,262 | +102% | 0 | 0 | — |
case-20 | pass→pass | 4,579 | 5,243 | +15% | 1 | 1 | 0% | 1,014 | 3,352 | +231% | 0 | 0 | — |
case-21 | pass→pass | 7,730 | 6,457 | -16% | 1 | 1 | 0% | 1,717 | 3,681 | +114% | 0 | 0 | — |
case-22 | pass→pass | 6,295 | 5,453 | -13% | 1 | 1 | 0% | 1,391 | 3,341 | +140% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of -100 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.