function identity<T>(value: T): T {
return value;
}
const a = identity('hello'); // inferred as string, not any
const b = identity(42); // inferred as number, not any
Topics
26
Classes: Access Modifiers, Abstract Classes & Implements
Conditional Types
Declaration Files
Decorators
Discriminated Unions & Exhaustiveness Checking
Enums
Function Types, Overloads & Optional/Default Parameters
Generics
Index Signatures & Record Types
Mapped Types
Migrating JavaScript to TypeScript
Modules
Namespaces & Declaration Merging
Never, Unknown & Void Types
Nullability: strictNullChecks, Optional Chaining & Nullish Coalescing
Primitive Types, Arrays & Tuples
Readonly, const Assertions & Immutability
Structural Typing & Duck Typing
Template Literal Types
tsconfig & Compiler Options
Type Assertions & Type Casting
Type Inference & Contextual Typing
Type Narrowing & Guards
Types & Interfaces
Union & Intersection Types
Utility Types
Generics
10 questions found
'any' disables type checking entirely, losing all safety and autocomplete. Generics let a function or type work with MULTIPLE specific types while preserving the actual relationship between input and output types — the compiler still knows exactly what type comes out based on what type went in.
Real-world example
Writing a single reusable getFirst() function that correctly preserves the specific array element type for any array passed in.
Type Inference & Contextual Typing
How do you declare a generic type parameter on a function, and how do you explicitly specify it when calling?
BeginnerAdd `<T>` (or any name) right after the function name to declare a type parameter, then use T within the parameter and return types; you can let TypeScript infer T from the arguments, or explicitly specify it in angle brackets at the call site.
function wrapInArray<T>(value: T): T[] {
return [value];
}
wrapInArray('hi'); // inferred: string[]
wrapInArray<number>(5); // explicit: number[]
Real-world example
Explicitly specifying a generic type argument when the compiler can't infer it confidently from the call alone, like an empty array literal.
Type Inference & Contextual Typing
How do you constrain a generic type parameter using 'extends' to require it has certain properties?
IntermediateWrite `<T extends SomeShape>` to restrict T to only types that are assignable to SomeShape, letting you safely access SomeShape's properties inside the function while still preserving T's more specific inferred type for the return value.
function getLength<T extends { length: number }>(item: T): number {
return item.length;
}
getLength('hello'); // OK -- strings have .length
getLength([1, 2, 3]); // OK -- arrays have .length
// getLength(42); // Error: number doesn't have .length
Real-world example
Writing a reusable utility that needs to access a common property (like .length or .id) across otherwise different input types.
Structural Typing & Duck Typing
How do you use a generic type parameter to constrain a second parameter to be a valid key of the first?
IntermediateUse `<T, K extends keyof T>` — this lets a function like a safe property-getter accept any key that ACTUALLY exists on T, catching typos or invalid keys at compile time, and lets the return type be precisely T[K].
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: 'Sam', age: 30 };
getProperty(user, 'name'); // OK, returns string
// getProperty(user, 'email'); // Error: 'email' is not a key of user
Real-world example
Writing a fully type-safe generic getter/setter utility that catches invalid property names at compile time.
Index Signatures & Record Types
How do you provide a default type for a generic parameter, similar to a default function parameter?
IntermediateWrite `<T = DefaultType>` after the type parameter name; if the caller doesn't explicitly specify T and it can't be inferred from context, the default type is used instead, similar in spirit to default function parameter values.
interface ApiResponse<T = unknown> {
data: T;
status: number;
}
const response: ApiResponse = { data: 'anything', status: 200 }; // T defaults to unknown
Real-world example
Providing a sensible fallback generic type for a widely-used utility type or interface when callers don't always need to specify it.
Never
Unknown & Void Types
How does generic type inference work across multiple arguments, and what happens when they conflict?
AdvancedTypeScript tries to find the single type T that's compatible with ALL usages across the arguments — when arguments suggest different types for the same T, TypeScript computes their union (the most general type compatible with every usage) rather than raising an immediate error, unless that union is truly incompatible with a constraint.
function combine<T>(a: T, b: T): T[] {
return [a, b];
}
combine(1, 2); // T inferred as number
combine(1, 'two'); // T inferred as string | number, NOT an error
Real-world example
Understanding why passing mismatched-looking arguments to a generic function widens the inferred type into a union rather than immediately failing.
Union & Intersection Types
How do you write a generic class, and how do its type parameters interact with its constructor and methods?
AdvancedDeclare the type parameter on the class itself (right after the class name), and it becomes available throughout the entire class body — fields, constructor, and methods can all reference it, and it's fixed to a specific type the moment you instantiate the class with 'new'.
class Box<T> {
private contents: T;
constructor(value: T) { this.contents = value; }
get(): T { return this.contents; }
set(value: T): void { this.contents = value; }
}
const numberBox = new Box<number>(42);
const stringBox = new Box('hello'); // T inferred as string from the constructor argument
Real-world example
Building a generic Repository<T>, Cache<T>, or Box<T> class reusable across many different entity types with full type safety.
Classes: Access Modifiers
Abstract Classes & Implements
How would you write a generic 'pick' utility function that mimics the built-in Pick<T, K> utility type, but as a runtime function with matching compile-time types?
AdvancedConstrain K to `keyof T` (and typically require it to be an array of such keys), then build the result object at runtime while typing the return value as `Pick<T, K[number]>`, so the function's actual runtime behavior and its compile-time type both correctly narrow to just the requested keys.
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const result = {} as Pick<T, K>;
keys.forEach(key => { result[key] = obj[key]; });
return result;
}
const user = { name: 'Sam', age: 30, email: 'sam@x.com' };
const picked = pick(user, ['name', 'age']); // { name: string; age: number }
Real-world example
Implementing a fully type-safe runtime pick() helper matching lodash's behavior but with precise TypeScript inference.
Mapped Types
What does 'variance' mean for generic types, and why does TypeScript's structural typing make some generic assignments unsound by default?
AdvancedVariance describes how subtyping relationships between type parameters affect subtyping of the generic type itself — TypeScript's structural, method-bivariant checking (for backward JS compatibility) allows some generic function parameter assignments that are technically UNSOUND (could cause a runtime type error) purely for practical ergonomic reasons, unlike stricter languages that enforce full soundness.
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
type Handler<T> = (item: T) => void;
let dogHandler: Handler<Dog> = (dog) => console.log(dog.breed);
let animalHandler: Handler<Animal> = dogHandler; // allowed, but technically unsound if called with a non-Dog Animal
Real-world example
Understanding a subtle class of bugs that TypeScript's structural type system deliberately permits for practical flexibility.
tsconfig & Compiler Options
How would you implement a generic type-safe EventEmitter where each event name maps to its own specific payload type?
AdvancedUse a generic type parameter constrained to a mapping object (Record of event names to payload types), and have on()/emit() methods keyed by `keyof EventMap`, so TypeScript enforces that each event's payload argument matches exactly what was declared for that event name.
type EventMap = { login: { userId: string }; logout: void };
class TypedEmitter<Events extends Record<string, any>> {
private listeners: { [K in keyof Events]?: ((payload: Events[K]) => void)[] } = {};
on<K extends keyof Events>(event: K, handler: (payload: Events[K]) => void) {
(this.listeners[event] ??= []).push(handler);
}
emit<K extends keyof Events>(event: K, payload: Events[K]) {
this.listeners[event]?.forEach(fn => fn(payload));
}
}
const emitter = new TypedEmitter<EventMap>();
emitter.on('login', (payload) => console.log(payload.userId)); // fully typed
Real-world example
Building a fully type-safe pub-sub or event bus system where mismatched event names or payload shapes are caught at compile time.
Mapped Types