Install
$ agentstack add skill-figueroaignacio-ignaciofigueroa-dev-typescript-advanced-types ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
TypeScript Advanced Types
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.
When to Use This Skill
- Building type-safe libraries or frameworks
- Creating reusable generic components
- Implementing complex type inference logic
- Designing type-safe API clients
- Building form validation systems
- Creating strongly-typed configuration objects
- Implementing type-safe state management
- Migrating JavaScript codebases to TypeScript
Core Concepts
1. Generics
Purpose: Create reusable, type-flexible components while maintaining type safety.
Basic Generic Function:
function identity(value: T): T {
return value;
}
const num = identity(42); // Type: number
const str = identity('hello'); // Type: string
const auto = identity(true); // Type inferred: boolean
Generic Constraints:
interface HasLength {
length: number;
}
function logLength(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:
function merge(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const merged = merge({ name: 'John' }, { age: 30 });
// Type: { name: string } & { age: number }
2. Conditional Types
Purpose: Create types that depend on conditions, enabling sophisticated type logic.
Basic Conditional Type:
type IsString = T extends string ? true : false;
type A = IsString; // true
type B = IsString; // false
Extracting Return Types:
type ReturnType = T extends (...args: any[]) => infer R ? R : never;
function getUser() {
return { id: 1, name: 'John' };
}
type User = ReturnType;
// Type: { id: number; name: string; }
Distributive Conditional Types:
type ToArray = T extends any ? T[] : never;
type StrOrNumArray = ToArray;
// Type: string[] | number[]
Nested Conditions:
type TypeName = 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"
type T2 = TypeName void>; // "function"
3. Mapped Types
Purpose: Transform existing types by iterating over their properties.
Basic Mapped Type:
type Readonly = {
readonly [P in keyof T]: T[P];
};
interface User {
id: number;
name: string;
}
type ReadonlyUser = Readonly;
// Type: { readonly id: number; readonly name: string; }
Optional Properties:
type Partial = {
[P in keyof T]?: T[P];
};
type PartialUser = Partial;
// Type: { id?: number; name?: string; }
Key Remapping:
type Getters = {
[K in keyof T as `get${Capitalize}`]: () => T[K];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters;
// Type: { getName: () => string; getAge: () => number; }
Filtering Properties:
type PickByType = {
[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;
// Type: { id: number; age: number; }
4. Template Literal Types
Purpose: Create string-based types with pattern matching and transformation.
Basic Template Literal:
type EventName = 'click' | 'focus' | 'blur';
type EventHandler = `on${Capitalize}`;
// Type: "onClick" | "onFocus" | "onBlur"
String Manipulation:
type UppercaseGreeting = Uppercase; // "HELLO"
type LowercaseGreeting = Lowercase; // "hello"
type CapitalizedName = Capitalize; // "John"
type UncapitalizedName = Uncapitalize; // "john"
Path Building:
type Path = T extends object
? {
[K in keyof T]: K extends string ? `${K}` | `${K}.${Path}` : never;
}[keyof T]
: never;
interface Config {
server: {
host: string;
port: number;
};
database: {
url: string;
};
}
type ConfigPath = Path;
// Type: "server" | "database" | "server.host" | "server.port" | "database.url"
5. Utility Types
Built-in Utility Types:
// Partial - Make all properties optional
type PartialUser = Partial;
// Required - Make all properties required
type RequiredUser = Required;
// Readonly - Make all properties readonly
type ReadonlyUser = Readonly;
// Pick - Select specific properties
type UserName = Pick;
// Omit - Remove specific properties
type UserWithoutPassword = Omit;
// Exclude - Exclude types from union
type T1 = Exclude; // "b" | "c"
// Extract - Extract types from union
type T2 = Extract; // "a" | "b"
// NonNullable - Exclude null and undefined
type T3 = NonNullable; // string
// Record - Create object type with keys K and values T
type PageInfo = Record;
Advanced Patterns
Pattern 1: Type-Safe Event Emitter
type EventMap = {
'user:created': { id: string; name: string };
'user:updated': { id: string };
'user:deleted': { id: string };
};
class TypedEventEmitter> {
private listeners: {
[K in keyof T]?: Array void>;
} = {};
on(event: K, callback: (data: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(callback);
}
emit(event: K, data: T[K]): void {
const callbacks = this.listeners[event];
if (callbacks) {
callbacks.forEach((callback) => callback(data));
}
}
}
const emitter = new TypedEventEmitter();
emitter.on('user:created', (data) => {
console.log(data.id, data.name); // Type-safe!
});
emitter.emit('user:created', { id: '1', name: 'John' });
// emitter.emit("user:created", { id: "1" }); // Error: missing 'name'
Pattern 2: Type-Safe API Client
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type EndpointConfig = {
'/users': {
GET: { response: User[] };
POST: { body: { name: string; email: string }; response: User };
};
'/users/:id': {
GET: { params: { id: string }; response: User };
PUT: { params: { id: string }; body: Partial; response: User };
DELETE: { params: { id: string }; response: void };
};
};
type ExtractParams = T extends { params: infer P } ? P : never;
type ExtractBody = T extends { body: infer B } ? B : never;
type ExtractResponse = T extends { response: infer R } ? R : never;
class APIClient>> {
async request(
path: Path,
method: Method,
...[options]: ExtractParams extends never
? ExtractBody extends never
? []
: [{ body: ExtractBody }]
: [
{
params: ExtractParams;
body?: ExtractBody;
},
]
): Promise> {
// Implementation here
return {} as any;
}
}
const api = new APIClient();
// Type-safe API calls
const users = await api.request('/users', 'GET');
// Type: User[]
const newUser = await api.request('/users', 'POST', {
body: { name: 'John', email: 'john@example.com' },
});
// Type: User
const user = await api.request('/users/:id', 'GET', {
params: { id: '123' },
});
// Type: User
Pattern 3: Builder Pattern with Type Safety
type BuilderState = {
[K in keyof T]: T[K] | undefined;
};
type RequiredKeys = {
[K in keyof T]-?: {} extends Pick ? never : K;
}[keyof T];
type OptionalKeys = {
[K in keyof T]-?: {} extends Pick ? K : never;
}[keyof T];
type IsComplete =
RequiredKeys extends keyof S ? (S[RequiredKeys] extends undefined ? false : true) : false;
class Builder = {}> {
private state: S = {} as S;
set(key: K, value: T[K]): Builder> {
this.state[key] = value;
return this as any;
}
build(this: IsComplete extends true ? this : never): T {
return this.state as T;
}
}
interface User {
id: string;
name: string;
email: string;
age?: number;
}
const builder = new Builder();
const user = builder.set('id', '1').set('name', 'John').set('email', 'john@example.com').build(); // OK: all required fields set
// const incomplete = builder
// .set("id", "1")
// .build(); // Error: missing required fields
Pattern 4: Deep Readonly/Partial
type DeepReadonly = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly
: T[P];
};
type DeepPartial = {
[P in keyof T]?: T[P] extends object
? T[P] extends Array
? Array>
: DeepPartial
: T[P];
};
interface Config {
server: {
host: string;
port: number;
ssl: {
enabled: boolean;
cert: string;
};
};
database: {
url: string;
pool: {
min: number;
max: number;
};
};
}
type ReadonlyConfig = DeepReadonly;
// All nested properties are readonly
type PartialConfig = DeepPartial;
// All nested properties are optional
Pattern 5: Type-Safe Form Validation
type ValidationRule = {
validate: (value: T) => boolean;
message: string;
};
type FieldValidation = {
[K in keyof T]?: ValidationRule[];
};
type ValidationErrors = {
[K in keyof T]?: string[];
};
class FormValidator> {
constructor(private rules: FieldValidation) {}
validate(data: T): ValidationErrors | null {
const errors: ValidationErrors = {};
let hasErrors = false;
for (const key in this.rules) {
const fieldRules = this.rules[key];
const value = data[key];
if (fieldRules) {
const fieldErrors: string[] = [];
for (const rule of fieldRules) {
if (!rule.validate(value)) {
fieldErrors.push(rule.message);
}
}
if (fieldErrors.length > 0) {
errors[key] = fieldErrors;
hasErrors = true;
}
}
}
return hasErrors ? errors : null;
}
}
interface LoginForm {
email: string;
password: string;
}
const validator = new FormValidator({
email: [
{
validate: (v) => v.includes('@'),
message: 'Email must contain @',
},
{
validate: (v) => v.length > 0,
message: 'Email is required',
},
],
password: [
{
validate: (v) => v.length >= 8,
message: 'Password must be at least 8 characters',
},
],
});
const errors = validator.validate({
email: 'invalid',
password: 'short',
});
// Type: { email?: string[]; password?: string[]; } | null
Pattern 6: Discriminated Unions
type Success = {
status: 'success';
data: T;
};
type Error = {
status: 'error';
error: string;
};
type Loading = {
status: 'loading';
};
type AsyncState = Success | Error | Loading;
function handleState(state: AsyncState): void {
switch (state.status) {
case 'success':
console.log(state.data); // Type: T
break;
case 'error':
console.log(state.error); // Type: string
break;
case 'loading':
console.log('Loading...');
break;
}
}
// Type-safe state machine
type State =
| { type: 'idle' }
| { type: 'fetching'; requestId: string }
| { type: 'success'; data: any }
| { type: 'error'; error: Error };
type Event =
| { type: 'FETCH'; requestId: string }
| { type: 'SUCCESS'; data: any }
| { type: 'ERROR'; error: Error }
| { type: 'RESET' };
function reducer(state: State, event: Event): State {
switch (state.type) {
case 'idle':
return event.type === 'FETCH' ? { type: 'fetching', requestId: event.requestId } : state;
case 'fetching':
if (event.type === 'SUCCESS') {
return { type: 'success', data: event.data };
}
if (event.type === 'ERROR') {
return { type: 'error', error: event.error };
}
return state;
case 'success':
case 'error':
return event.type === 'RESET' ? { type: 'idle' } : state;
}
}
Type Inference Techniques
1. Infer Keyword
// Extract array element type
type ElementType = T extends (infer U)[] ? U : never;
type NumArray = number[];
type Num = ElementType; // number
// Extract promise type
type PromiseType = T extends Promise ? U : never;
type AsyncNum = PromiseType>; // number
// Extract function parameters
type Parameters = T extends (...args: infer P) => any ? P : never;
function foo(a: string, b: number) {}
type FooParams = Parameters; // [string, number]
2. Type Guards
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function isArrayOf(value: unknown, guard: (item: unknown) => item is T): value is T[] {
return Array.isArray(value) && value.every(guard);
}
const data: unknown = ['a', 'b', 'c'];
if (isArrayOf(data, isString)) {
data.forEach((s) => s.toUpperCase()); // Type: string[]
}
3. Assertion Functions
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Not a string');
}
}
function processValue(value: unknown) {
assertIsString(value);
// value is now typed as string
console.log(value.toUpperCase());
}
Best Practices
- Use
unknownoverany: Enforce type checking - Prefer
interfacefor object shapes: Better error messages - Use
typefor unions and complex types: More flexible - Leverage type inference: Let TypeScript infer when possible
- Create helper types: Build reusable type utilities
- Use const assertions: Preserve literal types
- Avoid type assertions: Use type guards instead
- Document complex types: Add JSDoc comments
- Use strict mode: Enable all strict compiler options
- Test your types: Use type tests to verify type behavior
Type Testing
// Type assertion tests
type AssertEqual = [T] extends [U] ? ([U] extends [T] ? true : false) : false;
type Test1 = AssertEqual; // true
type Test2 = AssertEqual; // false
type Test3 = AssertEqual; // false
// Expect error helper
type ExpectError = T;
// Example usage
type ShouldError = ExpectError>;
Common Pitfalls
- Over-using
any: Defeats the purpose of TypeScript - Ignoring strict null checks: Can lead to runtime errors
- Too complex types: Can slow down compilation
- Not using discriminated unions: Misses type narrowing opportunities
- Forgetting readonly modifiers: Allows unintended mutations
- Circular type references: Can cause compiler errors
- Not handling edge cases: Like empty arrays or null values
Performance Considerations
- Avoid deeply nested conditional types
- Use simple types when possible
- Cache complex type computations
- Limit recursion depth in recursive types
- Use build tools to skip type checking in production
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: figueroaignacio
- Source: figueroaignacio/ignaciofigueroa.dev
- License: MIT
- Homepage: https://ignaciofigueroa.dev
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.