Difficulty: Advanced
Predict-the-output questions test your understanding of how TypeScript code actually behaves at runtime versus compile time. TypeScript adds a type layer on top of JavaScript, but all type information is erased at compile time. This means that type assertions, interfaces, type aliases, and generics have zero runtime impact - they exist only during development. Understanding this distinction is critical for predicting output correctly.
Many output questions focus on areas where TypeScript's type system and JavaScript's runtime behavior diverge. For example, TypeScript enums compile to real JavaScript objects (unless they are const enums), meaning you can access enum values and even reverse-map from values to names at runtime. Type assertions (`value as Type`) perform no runtime conversion - they only tell the compiler to treat a value as a different type. The value itself is unchanged.
Type narrowing is another frequent topic. TypeScript narrows types based on control flow analysis - after an `if (typeof x === 'string')` check, the compiler knows x is a string. But the narrowing only affects the type checker; the runtime behavior follows JavaScript rules. Questions often test whether you understand that `typeof null === 'object'`, `typeof NaN === 'number'`, and `typeof [] === 'object'`.
Structural typing means TypeScript checks type compatibility based on shape, not name. Two interfaces with the same properties are compatible even if they have different names. This leads to surprising behaviors in output questions where objects pass type checks despite being instances of different classes. At runtime, JavaScript uses reference identity for instanceof checks, not structural compatibility, which creates interesting interview scenarios.
// TypeScript enums compile to JavaScript objects
enum Direction {
Up = 0,
Down = 1,
Left = 2,
Right = 3
}
// Numeric enums support reverse mapping
console.log(Direction.Up);
console.log(Direction[0]);
console.log(Direction[Direction.Right]);
// String enums do NOT support reverse mapping
enum Color {
Red = 'RED',
Green = 'GREEN',
Blue = 'BLUE'
}
console.log(Color.Red);
console.log(typeof Color.Red);
// Enum is a real object at runtime
console.log(typeof Direction);
Numeric enums create a bidirectional mapping (name -> value and value -> name). String enums only map name -> value. Both are real objects at runtime.
// Type assertions do NOT change the runtime value
const value: any = '42';
const asNumber = value as number; // Still a string at runtime!
console.log(typeof asNumber);
console.log(asNumber);
// This would fail at runtime despite passing type check
// console.log(asNumber.toFixed(2)); // Runtime error: toFixed is not a function
// Type narrowing with typeof
function processValue(val: string | number): string {
if (typeof val === 'number') {
return `Number: ${val.toFixed(2)}`;
}
return `String: ${val.toUpperCase()}`;
}
console.log(processValue(3.14159));
console.log(processValue('hello'));
// typeof quirks
console.log(typeof null);
console.log(typeof undefined);
console.log(typeof NaN);
console.log(typeof []);
Type assertions (as Type) only affect compile-time types, not runtime values. The value '42' remains a string even after 'as number'. typeof quirks: null is 'object', NaN is 'number', arrays are 'object'.
// Structural typing: compatible shapes are assignable
class Point2D {
constructor(public x: number, public y: number) {}
}
class Point3D {
constructor(public x: number, public y: number, public z: number) {}
}
// TypeScript allows this because Point3D has all properties of Point2D
const p: Point2D = new Point3D(1, 2, 3);
console.log(`x: ${p.x}, y: ${p.y}`);
// But instanceof checks class identity, not structure
console.log(p instanceof Point2D);
console.log(p instanceof Point3D);
// Interface-based structural compatibility
interface HasLength {
length: number;
}
function getLength(item: HasLength): number {
return item.length;
}
// All of these work because they have a 'length' property
console.log(getLength('hello'));
console.log(getLength([1, 2, 3]));
console.log(getLength({ length: 42, extra: true }));
TypeScript uses structural typing - any object with the right shape is compatible. But instanceof checks the prototype chain, not the structure. Point3D instance is NOT a Point2D instance despite being structurally compatible.
type coercion, type narrowing, structural typing, enum behavior, type assertions, runtime vs compile-time