Generics & Advanced Types Interview Questions
Comprehensive generics & advanced types interview questions and answers for TypeScript. Prepare for your next job interview with expert guidance.
Questions Overview
1. What are generics in TypeScript and why are they useful?
Basic2. How do you use generic constraints in TypeScript?
Moderate3. What are generic interfaces and how do you implement them?
Moderate4. What are conditional types in TypeScript?
Advanced5. How do you use generic type inference?
Moderate6. What are type parameters in generics?
Basic7. How do you use generic classes in TypeScript?
Moderate8. What are mapped types and how do they work?
Advanced9. How do you use the infer keyword in conditional types?
Advanced10. What are generic type defaults?
Moderate11. How do you use multiple type parameters in generics?
Moderate12. What are index types and how do they work with generics?
Advanced13. How do you create generic type guards?
Advanced14. What are union and intersection types in generics?
Advanced15. How do you use generic type aliases?
Moderate16. How do you use generic constraints with unions and intersections?
Advanced17. What are branded types and how do they work with generics?
Advanced18. How do you use keyof with generics?
Moderate19. What are variadic tuple types?
Advanced20. How do you use template literal types with generics?
Advanced21. What are generic type assertions?
Advanced22. How do you use generic default types with constraints?
Advanced23. What are distributive conditional types?
Advanced24. How do you create type-safe event emitters with generics?
Advanced1. What are generics in TypeScript and why are they useful?
BasicGenerics are a way to create reusable components that can work with multiple types while maintaining type safety. They allow you to write functions, classes, and interfaces that can work with any data type while preserving type information. Example: function identity<T>(arg: T): T { return arg; }. Generics provide type safety, code reusability, and prevent code duplication.
2. How do you use generic constraints in TypeScript?
ModerateGeneric constraints limit what types can be used with a generic type using the 'extends' keyword. Example: function getLength<T extends { length: number }>(arg: T): number { return arg.length; }. This ensures that the generic type T must have a length property. Constraints help enforce type safety while maintaining flexibility.
3. What are generic interfaces and how do you implement them?
ModerateGeneric interfaces are interfaces that can work with multiple types. Example: interface Container<T> { value: T; getValue(): T; }. Implementation: class NumberContainer implements Container<number> { constructor(public value: number) {} getValue(): number { return this.value; } }. They enable type-safe, reusable interface definitions.
4. What are conditional types in TypeScript?
AdvancedConditional types select a type based on a condition, using syntax similar to ternary operators: T extends U ? X : Y. Example: type NonNullable<T> = T extends null | undefined ? never : T. They're powerful for creating complex type transformations and can be used with mapped types and unions.
5. How do you use generic type inference?
ModerateTypeScript can infer generic types from usage context. Example: function identity<T>(arg: T): T { return arg; } let output = identity('hello'); // T is inferred as string. Type inference reduces verbosity while maintaining type safety. The compiler uses argument types to determine generic type parameters.
6. What are type parameters in generics?
BasicType parameters are placeholders for types in generic definitions, conventionally denoted by T, U, K, V, etc. Example: function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; }. They can have constraints, defaults, and be used in multiple places within the generic definition.
7. How do you use generic classes in TypeScript?
ModerateGeneric classes use type parameters to create flexible, reusable class definitions. Example: class Stack<T> { private items: T[] = []; push(item: T) { this.items.push(item); } pop(): T | undefined { return this.items.pop(); } }. They maintain type safety while working with different data types.
8. What are mapped types and how do they work?
AdvancedMapped types create new types by transforming properties of existing types. Example: type Readonly<T> = { readonly [P in keyof T]: T[P] }. They can add/remove modifiers (readonly, optional) and transform property types. Mapped types are powerful for type transformations and creating utility types.
9. How do you use the infer keyword in conditional types?
AdvancedThe infer keyword extracts type information within conditional types. Example: type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any. This extracts the return type R from a function type. infer is powerful for type inference and extraction in complex scenarios.
10. What are generic type defaults?
ModerateGeneric type defaults provide fallback types when no type argument is specified. Example: interface Container<T = string> { value: T; }. When no type is provided, string is used: let container: Container = { value: 'hello' }. They help make generic types more convenient to use.
11. How do you use multiple type parameters in generics?
ModerateMultiple type parameters allow generics to work with multiple types simultaneously. Example: function pair<T, U>(first: T, second: U): [T, U] { return [first, second]; }. The order and naming of type parameters matter for readability and understanding the relationship between types.
12. What are index types and how do they work with generics?
AdvancedIndex types allow type-safe access to properties using dynamic keys. Example: function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }. This ensures type safety when accessing object properties dynamically.
13. How do you create generic type guards?
AdvancedGeneric type guards combine generics with type predicates. Example: function isOfType<T>(value: any, property: keyof T): value is T { return property in value; }. They provide type-safe runtime checks while maintaining generic type information.
14. What are union and intersection types in generics?
AdvancedUnion and intersection types can be used with generics to create flexible type combinations. Example: function process<T, U>(value: T | U): T & U { ... }. This allows working with types that can be either T or U while producing a type that has properties of both.
15. How do you use generic type aliases?
ModerateGeneric type aliases create reusable type definitions with type parameters. Example: type Result<T> = { success: boolean; data?: T; error?: string; }. They provide flexibility while maintaining type safety and can be used with unions, intersections, and mapped types.
16. How do you use generic constraints with unions and intersections?
AdvancedGeneric constraints can use union and intersection types to create complex type requirements. Example: function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U { return { ...obj1, ...obj2 }; }. This ensures type parameters meet specific criteria while allowing flexible combinations.
17. What are branded types and how do they work with generics?
AdvancedBranded types are nominal types created using intersection with unique symbols. Example: type Brand<T, B> = T & { __brand: B }; type USD = Brand<number, 'USD'>. They provide type safety by preventing mixing of similarly structured but semantically different types.
18. How do you use keyof with generics?
ModerateThe keyof operator can be used with generics to create type-safe property access. Example: function getNestedValue<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }. This ensures type safety when accessing object properties dynamically.
19. What are variadic tuple types?
AdvancedVariadic tuple types allow working with tuples of variable length in a type-safe way. Example: type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]. They're useful for type-safe array operations and function parameter handling.
20. How do you use template literal types with generics?
AdvancedTemplate literal types can be combined with generics to create dynamic string types. Example: type PropEventType<T extends string> = `${T}Changed`; This creates new string literal types based on generic parameters.
21. What are generic type assertions?
AdvancedGeneric type assertions allow specifying type parameters when using type assertions. Example: function create<T>(factory: { new(): T }): T { return new factory() as T; }. They provide type safety when working with dynamic type creation.
22. How do you use generic default types with constraints?
AdvancedGeneric default types can be combined with constraints to provide flexible yet safe defaults. Example: interface Container<T extends object = { id: string }> { data: T; }. This ensures the default type meets the constraint while allowing other compatible types.
23. What are distributive conditional types?
AdvancedDistributive conditional types apply conditions to each member of a union type. Example: type ToArray<T> = T extends any ? T[] : never; type NumberOrStringArray = ToArray<number | string>; // number[] | string[]. They're useful for transforming union types.
24. How do you create type-safe event emitters with generics?
AdvancedGeneric event emitters provide type safety for event handling. Example: class EventEmitter<Events extends Record<string, any>> { emit<E extends keyof Events>(event: E, data: Events[E]): void { ... } }. This ensures event names and data types match at compile time.