Designs complex generic types, refactors `any` types to strict alternatives, creates type guards and utility types, and resolves TypeScript compiler errors. Use when the user asks about TypeScript (TS) types, generics, type inference, type guards, removing `any` types, strict typing, type errors, `infer`, `extends`, conditional types, mapped types, template literal types, branded/opaque types, or utility types like `Partial`, `Record`, `ReturnType`, and `Awaited`.
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Generics allow you to create reusable components that work with multiple types while maintaining type safety. They're essential for building flexible, type-safe APIs.
// Without generics - loses type information
function identity(value: any): any {
return value;
}
// With generics - preserves type
function identity<T>(value: T): T {
return value;
}
const num = identity(42); // Type: number (inferred)
const str = identity("hello"); // Type: string (inferred)
const explicit = identity<boolean>(true); // Type: boolean (explicit)When the type of one parameter depends on another, use generics:
// The return type depends on what keys exist in the config
const createComponent = <TConfig extends Record<string, string>>(
config: TConfig,
) => {
return (variant: keyof TConfig, ...otherClasses: string[]): string => {
return config[variant] + " " + otherClasses.join(" ");
};
};
// TConfig is inferred as { primary: string; secondary: string }
const getButtonClasses = createComponent({
primary: "bg-blue-300",
secondary: "bg-green-300",
});
// variant must be "primary" | "secondary"
getButtonClasses("primary", "px-4"); // OK
getButtonClasses("tertiary", "px-4"); // Error: "tertiary" not in keysextendsConstrain generics to ensure they have required properties:
// Unconstrained - TFunc could be anything
type WrapFunction<TFunc> = (...args: any[]) => any;
// Constrained - TFunc must be a function
type WrapFunction<TFunc extends (...args: any) => any> = (
...args: Parameters<TFunc>
) => ReturnType<TFunc>;// Without constraint
function getLength<T>(item: T): number {
return item.length; // Error: Property 'length' does not exist on type 'T'
}
// With constraint
function getLength<T extends { length: number }>(item: T): number {
return item.length; // OK - we know T has length
}
getLength("hello"); // 5
getLength([1, 2, 3]); // 3
getLength({ length: 10 }); // 10
getLength(42); // Error: number doesn't have lengthProvide defaults for optional type parameters:
type WrapFunction<
TFunc extends (...args: any) => any,
TAdditional = {} // Default to empty object
> = (
...args: Parameters<TFunc>
) => Promise<Awaited<ReturnType<TFunc>> & TAdditional>;
// Can use without TAdditional
type BasicWrapper = WrapFunction<typeof fetchUser>;
// Or with TAdditional
type ExtendedWrapper = WrapFunction<typeof fetchUser, { meta: string }>;TypeScript infers generic types from usage:
// Generic is inferred from the argument passed
const createComponent = <TConfig>(config: TConfig) => {
return config;
};
// TConfig is inferred as { primary: string; secondary: string }
const component = createComponent({
primary: "bg-blue-300",
secondary: "bg-green-300",
});If you don't USE the generic in arguments, it defaults to unknown:
// BAD - TConfig isn't used in arguments, defaults to unknown
const createComponent = <TConfig>(config: Record<string, string>) => {
// TConfig is unknown here
};
// GOOD - TConfig IS the argument type
const createComponent = <TConfig extends Record<string, string>>(
config: TConfig,
) => {
// TConfig is inferred from what's passed
};Use multiple parameters for related but distinct types:
function map<TInput, TOutput>(
items: TInput[],
transform: (item: TInput) => TOutput
): TOutput[] {
return items.map(transform);
}
// Both TInput and TOutput are inferred
const numbers = map(["1", "2", "3"], (s) => parseInt(s));
// TInput: string, TOutput: number, Result: number[]keyof with GenericsCombine keyof with generics for type-safe property access:
function getProperty<TObj, TKey extends keyof TObj>(
obj: TObj,
key: TKey
): TObj[TKey] {
return obj[key];
}
const user = { name: "Alice", age: 30 };
const name = getProperty(user, "name"); // Type: string
const age = getProperty(user, "age"); // Type: number
const invalid = getProperty(user, "email"); // Error: "email" not in keyofclass Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
map<U>(transform: (value: T) => U): Container<U> {
return new Container(transform(this.value));
}
}
const numContainer = new Container(42);
const strContainer = numContainer.map((n) => n.toString());
// strContainer is Container<string>// A factory that creates type-safe component class generators
export const createComponent = <TConfig extends Record<string, string>>(
config: TConfig,
) => {
// Return a function that requires valid variant keys
return (variant: keyof TConfig, ...otherClasses: string[]): string => {
return config[variant] + " " + otherClasses.join(" ");
};
};
// Usage
const getButtonClasses = createComponent({
primary: "bg-blue-500 text-white",
secondary: "bg-gray-200 text-gray-800",
danger: "bg-red-500 text-white",
});
// Type-safe: variant must be "primary" | "secondary" | "danger"
const classes = getButtonClasses("primary", "px-4", "py-2");
// Result: "bg-blue-500 text-white px-4 py-2"
// Type error on invalid variant
getButtonClasses("invalid"); // Error!// BAD - generic provides no value
function greet<T extends string>(name: T): string {
return `Hello, ${name}`;
}
// GOOD - just use string
function greet(name: string): string {
return `Hello, ${name}`;
}// BAD - overly specific constraint
function process<T extends { id: string; name: string; email: string }>(
obj: T
): void {}
// GOOD - only require what you actually use
function process<T extends { id: string }>(obj: T): void {}// BAD - accessing property that might not exist
function getName<T>(obj: T): string {
return obj.name; // Error: Property 'name' does not exist
}
// GOOD - constrain to types that have name
function getName<T extends { name: string }>(obj: T): string {
return obj.name;
}