HTML & JS Topic
Top 30 TypeScript Interview Questions & Answers
TypeScript Fundamentals
1. What is TypeScript and why use it over JavaScript?
Answer: TypeScript is a statically typed superset of JavaScript that compiles to plain JavaScript. Key benefits:
-
Static typing with type inference
-
Early error detection at compile time
-
Enhanced IDE support (autocomplete, refactoring)
-
Better documentation through type annotations
-
ECMAScript features (even if target JS doesn't support them)
-
Interfaces and generics for scalable architecture
typescript
// JavaScript vs TypeScript
// JavaScript: Runtime error
function add(a, b) { return a + b; }
add('5', 3); // '53' (unexpected)
// TypeScript: Compile-time error
function add(a: number, b: number): number {
return a + b;
}
add('5', 3); // Error: Argument of type 'string' is not assignable
2. What are the basic types in TypeScript?
Answer: TypeScript extends JavaScript types:
typescript
// Primitive types
let isDone: boolean = false;
let count: number = 42;
let name: string = "TypeScript";
// Arrays
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // Generic syntax
// Tuples
let tuple: [string, number] = ["hello", 10];
// Enum
enum Color { Red, Green, Blue };
let c: Color = Color.Green;
// Any (opt-out of type checking)
let notSure: any = 4;
notSure = "maybe a string";
// Unknown (type-safe counterpart of any)
let uncertain: unknown = 30;
// Void (no return value)
function warn(): void { console.log("warning"); }
// Never (function never returns)
function error(message: string): never {
throw new Error(message);
}
// Object (non-primitive)
let obj: object = { key: "value" };
3. Explain the difference between interface and type
Answer:
| Aspect | interface |
type |
|---|---|---|
| Extends | Can be extended | Can use intersections |
| Implements | Can be implemented by class | Cannot be implemented |
| Declaration merging | Supported | Not supported |
| Utility | Better for object shapes | More flexible (unions, tuples, primitives) |
| Performance | Slightly faster in some cases | Similar |
typescript
// Interface
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
// Type alias
type Animal2 = {
name: string;
};
type Bear2 = Animal2 & {
honey: boolean
};
// Interface declaration merging
interface Window {
title: string;
}
interface Window {
ts: TypeScriptAPI;
}
// Result: Window has both title and ts properties
// Type cannot do this - would be duplicate identifier
4. What are generics and why are they useful?
Answer: Generics create reusable components that work with multiple types while maintaining type safety.
typescript
// Without generics - loses type information
function identity(arg: any): any {
return arg;
}
// With generics - preserves type information
function identity<T>(arg: T): T {
return arg;
}
// Usage
let output = identity<string>("myString"); // Type: string
let output2 = identity(42); // Type inference: number
// Generic interfaces
interface GenericIdentityFn<T> {
(arg: T): T;
}
// Generic classes
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
// Constrained generics
function loggingIdentity<T extends { length: number }>(arg: T): T {
console.log(arg.length);
return arg;
}
5. What are union and intersection types?
Answer:
typescript
// Union type (A OR B)
type Status = "active" | "inactive" | "pending";
type ID = string | number;
function printId(id: ID) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed(2));
}
}
// Intersection type (A AND B)
interface BusinessPartner {
name: string;
credit: number;
}
interface Identity {
id: string;
name: string;
}
type Employee = BusinessPartner & Identity;
const employee: Employee = {
id: "123",
name: "John",
credit: 1000
};
Intermediate TypeScript
6. Explain type guards and narrowing
Answer: Type guards help TypeScript infer more specific types within conditional blocks.
typescript
// typeof guard
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return " ".repeat(padding) + value;
}
return padding + value; // padding is string here
}
// instanceof guard
class Bird {
fly() { console.log("flying"); }
}
class Fish {
swim() { console.log("swimming"); }
}
function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}
// in operator guard
interface Dog { bark(): void; }
interface Cat { meow(): void; }
function makeSound(animal: Dog | Cat) {
if ("bark" in animal) {
animal.bark();
} else {
animal.meow();
}
}
// Custom type guard (user-defined)
function isString(x: any): x is string {
return typeof x === "string";
}
// Discriminated unions
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
function area(s: Shape) {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "rectangle": return s.width * s.height;
}
}
7. What are decorators and how do you use them?
Answer: Decorators are special declarations that can modify classes, methods, properties, or parameters.
typescript
// Class decorator
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
// Method decorator
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with args: ${args}`);
return originalMethod.apply(this, args);
};
}
class Calculator {
@log
add(x: number, y: number): number {
return x + y;
}
}
// Property decorator
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value = target[propertyKey];
const getter = () => value;
const setter = (newVal: any) => {
value = formatString.replace("{value}", newVal);
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter
});
};
}
// Parameter decorator
function validate(target: any, propertyKey: string, parameterIndex: number) {
// Validation logic here
}
8. Explain the keyof and typeof operators
Answer:
typescript
// keyof: Get keys of a type/interface
interface Person {
name: string;
age: number;
location: string;
}
type PersonKeys = keyof Person; // "name" | "age" | "location"
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// typeof: Get type of a value
const person = { name: "Alice", age: 30 };
type PersonType = typeof person;
// Equivalent to: type PersonType = { name: string; age: number; }
// Combined usage
const settings = {
theme: "dark",
language: "en",
version: "1.0"
} as const; // as const makes it readonly
type Settings = typeof settings;
// type Settings = { readonly theme: "dark"; readonly language: "en"; readonly version: "1.0"; }
type SettingKeys = keyof Settings;
// type SettingKeys = "theme" | "language" | "version"
9. What are conditional types?
Answer: Types that express non-uniform type mappings based on conditions.
typescript
// Basic conditional type
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Extract from union
type ExtractType<T, U> = T extends U ? T : never;
type Result = ExtractType<string | number | boolean, string | number>;
// Result: string | number
// Exclude from union
type ExcludeType<T, U> = T extends U ? never : T;
type Result2 = ExcludeType<string | number | boolean, string>;
// Result2: number | boolean
// Infer keyword (extract type from generic)
type ArrayElementType<T> = T extends (infer U)[] ? U : never;
type Element = ArrayElementType<string[]>; // string
// ReturnType utility (built-in using conditional types)
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type FuncReturn = MyReturnType<() => string>; // string
// Flatten array type
type Flatten<T> = T extends any[] ? T[number] : T;
type Str = Flatten<string[]>; // string
10. Explain mapped types
Answer: Create new types by transforming properties of existing types.
typescript
// Make all properties optional
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Make all properties required
type Required<T> = {
[P in keyof T]-?: T[P];
};
// Make all properties readonly
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Pick specific properties
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// Record type (create object type with specific key/value types)
type Record<K extends keyof any, T> = {
[P in K]: T;
};
// Custom mapped type
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
interface User {
name: string;
age: number;
}
type NullableUser = Nullable<User>;
// { name: string | null; age: number | null; }
// Key remapping (TypeScript 4.1+)
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number; }
Advanced TypeScript
11. What are declaration files (.d.ts) and ambient declarations?
Answer: Declaration files provide type information for existing JavaScript code.
typescript
// user.d.ts (declaration file)
declare interface User {
id: number;
name: string;
}
declare function getUser(id: number): User;
declare const DEFAULT_USER: User;
// Usage in .ts file
const user = getUser(1); // TypeScript knows this returns User
// Global declarations (global.d.ts)
declare global {
interface Window {
myApp: any;
}
const __DEV__: boolean;
}
// Module declarations
declare module "my-module" {
export function doSomething(): void;
export const value: number;
}
// Shorthand ambient modules (for CSS, images, etc.)
declare module "*.css";
declare module "*.png";
declare module "*.svg" {
const content: string;
export default content;
}
12. Explain variance in TypeScript (covariance, contravariance, bivariance)
Answer: How subtyping relates to generics/complex types.
typescript
// Covariance: Preserves subtype relationship (default for arrays)
type Animal = { name: string };
type Dog = Animal & { breed: string };
let animals: Animal[] = [];
let dogs: Dog[] = [];
animals = dogs; // OK - covariance
// dogs = animals; // Error
// Contravariance: Reverses subtype relationship (function parameters)
type AnimalProcessor = (animal: Animal) => void;
type DogProcessor = (dog: Dog) => void;
let processAnimal: AnimalProcessor = (a: Animal) => {};
let processDog: DogProcessor = (d: Dog) => {};
// processDog = processAnimal; // OK - contravariant in parameter position
// processAnimal = processDog; // Error
// Invariance: Requires exact type (mutable arrays)
// Bivariance: Both accepted (TypeScript's default for function parameters in --strict mode off)
// Use variance annotations (in/out) with conditional types
interface Container<in T> { // contravariant
consume(value: T): void;
}
interface Producer<out T> { // covariant
produce(): T;
}
13. What are template literal types?
Answer: (TypeScript 4.1+) Types based on template literal strings.
typescript
type EventName<T extends string> = `${T}Changed`;
type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`;
// With unions
type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";
type Alignment = `${VerticalAlignment}-${HorizontalAlignment}`;
// "top-left" | "top-center" | "top-right" | "middle-left" | ...
// Infer substitution positions
type ExtractVerb<T> = T extends `${infer Verb}${string}` ? Verb : never;
type Verb = ExtractVerb<"getUser" | "setUser" | "deleteUser">; // "get" | "set" | "delete"
// Advanced: Path manipulation
type Path = `/${string}`;
type ApiRoute = `/api/${string}`;
// Utility types with template literals
type Getter<T extends string> = `get${Capitalize<T>}`;
type Setters<T extends string> = `set${Capitalize<T>}`;
14. Explain the satisfies operator
Answer: (TypeScript 4.9+) Validates expression type without changing inferred type.
typescript
// Without satisfies - loses literal types
const colors = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF"
}; // Type: { red: string; green: string; blue: string }
// With satisfies - preserves literal types
const colors2 = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF"
} as const; // Type: { readonly red: "#FF0000"; readonly green: "#00FF00"; readonly blue: "#0000FF"; }
// satisfies operator (better for complex cases)
const colors3 = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF"
} satisfies Record<string, `#${string}`>;
// Use case: Partial type checking without widening
interface Config {
width: number;
height: number;
color?: string;
}
const config = {
width: 100,
height: 200,
color: "#FF0000"
} satisfies Partial<Config>;
// Type: { width: number; height: number; color: string }
// Not: Partial<Config> (which would make all properties optional)
15. What are readonly arrays and tuples?
Answer: Immutable versions of array/tuple types.
typescript
// Readonly array
const numbers: readonly number[] = [1, 2, 3];
// numbers.push(4); // Error
// numbers[0] = 10; // Error
// Readonly tuple
type Point = readonly [number, number];
const origin: Point = [0, 0];
// origin[0] = 1; // Error
// const assertions (more strict)
const arr = [1, 2, 3] as const;
// arr is readonly [1, 2, 3] - literal readonly tuple
// Readonly utility type
interface Book {
title: string;
author: string;
}
const book: Readonly<Book> = { title: "TS", author: "Microsoft" };
// book.title = "New Title"; // Error
// Deep readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
TypeScript with Classes & OOP
16. What are abstract classes in TypeScript?
Answer: Classes that cannot be instantiated directly, meant to be subclassed.
typescript
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("moving...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("bark");
}
}
// const animal = new Animal(); // Error: Cannot create instance of abstract class
const dog = new Dog();
dog.makeSound(); // "bark"
dog.move(); // "moving..."
// Abstract properties
abstract class Department {
abstract name: string;
abstract printName(): void;
constructor(protected employees: string[] = []) {}
addEmployee(employee: string) {
this.employees.push(employee);
}
}
class ITDepartment extends Department {
constructor(public name: string) {
super();
}
printName(): void {
console.log(`Department: ${this.name}`);
}
}
17. Explain access modifiers (public, private, protected)
Answer: Control visibility of class members.
typescript
class Employee {
public name: string; // Accessible anywhere (default)
private salary: number; // Accessible only within class
protected employeeId: string; // Accessible within class and subclasses
constructor(name: string, salary: number) {
this.name = name;
this.salary = salary;
this.employeeId = this.generateId();
}
private generateId(): string {
return `EMP-${Date.now()}`;
}
public getSalaryInfo(): string {
return `Salary: $${this.salary}`; // Can access private member
}
}
class Manager extends Employee {
constructor(name: string, salary: number, public department: string) {
super(name, salary);
}
public getEmployeeInfo(): string {
// Can access protected member
return `${this.name} (${this.employeeId}) manages ${this.department}`;
// Cannot access this.salary (private)
}
}
// ECMAScript private fields (# syntax)
class ModernEmployee {
#secretCode: string; // Truly private (runtime enforcement)
constructor() {
this.#secretCode = "1234";
}
}
18. What are static members?
Answer: Members that belong to the class itself, not instances.
typescript
class MathUtils {
static readonly PI = 3.14159;
static calculateCircumference(radius: number): number {
return 2 * this.PI * radius;
}
static createFromDiameter(diameter: number): MathUtils {
return new MathUtils(diameter / 2);
}
constructor(public radius: number) {}
}
// Access via class name, not instance
console.log(MathUtils.PI);
console.log(MathUtils.calculateCircumference(10));
// Static blocks (TypeScript 4.4+)
class Database {
static connection: string;
static {
// Initialize static members
this.connection = "localhost:5432";
console.log("Database initialized");
}
}
19. Explain method overloading in TypeScript
Answer: Multiple function signatures for the same implementation.
typescript
class Calculator {
// Overload signatures
add(a: number, b: number): number;
add(a: string, b: string): string;
add(a: number, b: string): string;
add(a: string, b: number): string;
// Implementation signature (not publicly visible)
add(a: number | string, b: number | string): number | string {
if (typeof a === "number" && typeof b === "number") {
return a + b;
}
return `${a}${b}`;
}
}
const calc = new Calculator();
calc.add(1, 2); // number
calc.add("1", "2"); // string
calc.add(1, "2"); // string
// Function overloading
function formatDate(timestamp: number): string;
function formatDate(date: Date): string;
function formatDate(value: number | Date): string {
const date = typeof value === "number" ? new Date(value) : value;
return date.toISOString();
}
20. What are mixins in TypeScript?
Answer: Pattern for composing classes from multiple base classes.
typescript
// Constructor type
type Constructor<T = {}> = new (...args: any[]) => T;
// Mixin functions
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActive = false;
activate() {
this.isActive = true;
}
deactivate() {
this.isActive = false;
}
};
}
// Base class
class User {
constructor(public name: string) {}
}
// Apply mixins
const TimestampedActivatableUser = Timestamped(Activatable(User));
const user = new TimestampedActivatableUser("Alice");
console.log(user.name); // "Alice"
console.log(user.timestamp); // 1633046400000
user.activate();
console.log(user.isActive); // true
TypeScript Configuration & Tooling
21. What are the important compiler options in tsconfig.json?
Answer:
json
{
"compilerOptions": {
/* Basic Options */
"target": "ES2020", // JS version to compile to
"module": "commonjs", // Module system
"lib": ["ES2020", "DOM"], // Library files to include
"outDir": "./dist", // Output directory
"rootDir": "./src", // Source directory
/* Strictness Options */
"strict": true, // Enable all strict options
"noImplicitAny": true, // Error on implied 'any'
"strictNullChecks": true, // Null/undefined checking
"strictFunctionTypes": true, // Strict function parameter checking
"strictPropertyInitialization": true, // Class properties must be initialized
/* Module Resolution */
"moduleResolution": "node", // How to resolve modules
"baseUrl": "./", // Base directory for module resolution
"paths": { // Path mapping
"@/*": ["src/*"]
},
"esModuleInterop": true, // Better CommonJS/ES module interop
"allowSyntheticDefaultImports": true,
/* Advanced */
"declaration": true, // Generate .d.ts files
"sourceMap": true, // Generate source maps
"experimentalDecorators": true, // Enable decorators
"emitDecoratorMetadata": true,
/* Type Checking */
"noUnusedLocals": true, // Error on unused locals
"noUnusedParameters": true, // Error on unused parameters
"noImplicitReturns": true, // Error if not all code paths return
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
22. Explain module resolution in TypeScript
Answer: How TypeScript finds files referenced in import statements.
typescript
// Classic vs Node resolution
// Classic: Looks for .ts, .d.ts, and follows tsconfig.json paths
// Node: Follows Node.js module resolution algorithm
// Relative imports
import { Component } from './components/Component';
// Non-relative imports (looks in node_modules)
import React from 'react';
// Path mapping (tsconfig.json)
{
"paths": {
"@app/*": ["src/app/*"],
"@utils/*": ["src/utils/*"]
}
}
// Usage
import { api } from '@app/api';
import { format } from '@utils/helpers';
// Module resolution strategies affect:
// - import statement resolution
// /// <reference path="..." /> directives
// BaseUrl and paths configuratio
n
23. What are triple-slash directives?
Answer: Single-line comments containing XML tags used as compiler directives.
typescript
/// <reference path="..." /> // Dependency declaration
/// <reference types="..." /> // Type dependency
/// <reference lib="..." /> // Built-in lib inclusion
/// <amd-module name="..." /> // AMD module name
/// <amd-dependency path="..." /> // AMD dependency (deprecated)
// Example: Including type definitions
/// <reference types="node" />
/// <reference path="custom.d.ts" />
// These are largely superseded by tsconfig.json and import statements
// Still useful for:
// - Global type declarations ordering
// - UMD module declarations
// - Legacy codebases
TypeScript with Frameworks
24. How does TypeScript work with React?
Answer:
typescript
// Functional components
interface Props {
name: string;
age?: number;
}
const UserCard: React.FC<Props> = ({ name, age = 18 }) => {
return <div>{name} ({age})</div>;
};
// With hooks
const UserList: React.FC = () => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
fetchUsers();
}, []);
return (
<div>
{users.map(user => (
<UserCard key={user.id} name={user.name} />
))}
</div>
);
};
// Class components
interface State {
count: number;
}
class Counter extends React.Component<{}, State> {
state: State = { count: 0 };
increment = () => {
this.setState(prev => ({ count: prev.count + 1 }));
};
render() {
return (
<button onClick={this.increment}>
Count: {this.state.count}
</button>
);
}
}
// Generic components
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
25. How to type API responses in TypeScript?
Answer:
typescript
// Basic interface
interface User {
id: number;
name: string;
email: string;
}
// Using fetch with async/await
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data: User = await response.json();
return data;
}
// Generic API response type
interface ApiResponse<T> {
data: T;
status: number;
message: string;
timestamp: string;
}
// Zod for runtime validation (recommended)
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
async function fetchUserSafe(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
return UserSchema.parse(data); // Throws if invalid
}
// Partial types for updates
function updateUser(id: number, updates: Partial<User>) {
return fetch(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates)
});
}
Advanced Patterns & Best Practices
26. What is the as const assertion?
Answer: Makes an object/reference deeply readonly with literal types.
typescript
// Without as const
const colors = ["red", "green", "blue"];
// Type: string[]
// With as const
const colors2 = ["red", "green", "blue"] as const;
// Type: readonly ["red", "green", "blue"]
// Object properties become readonly literals
const user = {
name: "John",
age: 30,
address: {
city: "NYC"
}
} as const;
// user.name = "Jane"; // Error
// user.address.city = "LA"; // Error
// Use with typeof for type extraction
const ROUTES = {
home: "/",
about: "/about",
contact: "/contact"
} as const;
type Route = typeof ROUTES[keyof typeof ROUTES];
// Type: "/" | "/about" | "/contact"
// Function that returns literal types
function getStatus() {
return "success" as const;
}
// Return type: "success" (not string)
27. Explain the infer keyword in depth
Answer: Used in conditional types to extract and capture types.
typescript
// Extract return type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Extract parameter types
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
// Extract array element type
type ArrayElement<T> = T extends (infer U)[] ? U : never;
// Extract promise type
type PromiseType<T> = T extends Promise<infer U> ? U : never;
// Extract first argument
type FirstArg<T> = T extends (arg: infer A, ...args: any[]) => any ? A : never;
// Extract property type
type PropType<T, K extends keyof T> = T extends { [P in K]: infer U } ? U : never;
// Recursive extraction
type DeepArrayElement<T> =
T extends (infer U)[] ? DeepArrayElement<U> : T;
// Multiple inference points
type FunctionComponents<T> =
T extends (...args: infer Args) => infer Return
? { args: Args, return: Return }
: never;
28. What are branded/nominal types?
Answer: Creating distinct types for values with the same underlying structure.
typescript
// Using intersection with unique symbol
type UserId = string & { readonly brand: unique symbol };
type ProductId = string & { readonly brand: unique symbol };
function createUserId(id: string): UserId {
return id as UserId;
}
function createProductId(id: string): ProductId {
return id as ProductId;
}
const userId = createUserId("123");
const productId = createProductId("123");
// These are not interchangeable
// function getUser(id: UserId) { ... }
// getUser(productId); // Error
// Alternative with interfaces
interface Brand<T extends string> {
readonly brand: T;
}
type Branded<T, B extends string> = T & Brand<B>;
type Euros = Branded<number, "Euros">;
type Dollars = Branded<number, "Dollars">;
function addEuros(a: Euros, b: Euros): Euros {
return (a + b) as Euros;
}
// Runtime validation
function validateEuros(amount: number): Euros {
if (amount < 0) throw new Error("Invalid amount");
return amount as Euros;
}
29. How to implement recursive types?
Answer: Types that reference themselves.
typescript
// Recursive type alias
type Json =
| string
| number
| boolean
| null
| Json[]
| { [key: string]: Json };
// Tree structure
type TreeNode<T> = {
value: T;
children: TreeNode<T>[];
};
// Linked list
type LinkedList<T> = {
value: T;
next: LinkedList<T> | null;
};
// Recursive type with condition
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Recursive utility type for readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// Nested arrays
type NestedArray<T> = T | NestedArray<T>[];
const nested: NestedArray<number> = [1, [2, [3, 4]], 5];
30. What are template literal types with inference patterns?
Answer: Advanced template literal manipulation.
typescript
// Parse URL pattern
type ParseUrl<T extends string> =
T extends `${infer Protocol}://${infer Host}/${infer Path}`
? { protocol: Protocol, host: Host, path: Path }
: never;
type Url = ParseUrl<"https://example.com/api/users">;
// { protocol: "https", host: "example.com", path: "api/users" }
// Extract route parameters
type ExtractParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractParams<Rest>
: T extends `${string}:${infer Param}`
? Param
: never;
type Params = ExtractParams<"/users/:id/posts/:postId">;
// "id" | "postId"
// String manipulation
type Capitalize<S extends string> =
S extends `${infer First}${infer Rest}`
? `${Uppercase<First>}${Rest}`
: S;
type Uncapitalize<S extends string> =
S extends `${infer First}${infer Rest}`
? `${Lowercase<First>}${Rest}`
: S;
// Join string tuple
type Join<T extends string[], D extends string> =
T extends [] ? '' :
T extends [infer F] ? F :
T extends [infer F, ...infer R]
? F extends string
? `${F}${D}${Join<R extends string[] ? R : [], D>}`
: never
: string;
Bonus: Common TypeScript Gotchas & Solutions
1. Excess Property Checking
typescript
interface Point { x: number; y: number; }
const p: Point = { x: 1, y: 2, z: 3 }; // Error
// Solutions:
const obj = { x: 1, y: 2, z: 3 };
const p2: Point = obj; // OK - fresh object
const p3: Point = { x: 1, y: 2, z: 3 } as Point; // Type assertion
2. Index Signatures vs Record
typescript
// Index signature (all string keys must be number)
interface StringArray {
[index: string]: number;
}
// Record utility (same but more readable)
type StringArray2 = Record<string, number>;
3. Type vs Interface for Functions
typescript
// Interface for functions
interface SearchFunc {
(source: string, subString: string): boolean;
}
// Type alias for functions
type SearchFunc2 = (source: string, subString: string) => boolean;
Comments (0)
No comments yet. Be the first to comment!