TypeScript Challenges
Pick the correct answer for each TypeScript question. Instant feedback — green = correct, red = wrong. Hint available any time.
Challenge 1
Beginner
Type Annotations
Solved
What happens at compile time?
TypeScript enforces types at compile time. What does the compiler report for this code?
let score: number = 42;
score = "high";
Answer: C — Compile-time type error
TypeScript's type system catches this at compile time, not at runtime. The compiler reports:
TypeScript's type system catches this at compile time, not at runtime. The compiler reports:
Type 'string' is not assignable to type 'number'. This prevents a whole class of bugs before the code ever runs. Note: TypeScript compiles to JavaScript, so there are no TypeScript-specific runtime errors — all type enforcement happens before compilation.
Challenge 2
Beginner
Union Types
Solved
Which assignment is NOT valid?
A union type allows a variable to hold one of several types. Which value would cause a TypeScript error?
type ID = string | number;
let userId: ID;
Answer: C — userId = true
The union type
The union type
string | number accepts only strings and numbers. true is a boolean, which is not part of the union, so TypeScript reports: Type 'boolean' is not assignable to type 'ID'. Option D is valid because String(99) returns the string "99".
Challenge 3
Intermediate
Generics
Solved
What is the inferred return type?
TypeScript infers the return type of generic functions from the argument. What type does
wrap("hello") return?function wrap<T>(value: T): T[] {
return [value];
}
const result = wrap("hello");
Answer: C — string[]
TypeScript performs type argument inference: when you call
TypeScript performs type argument inference: when you call
wrap("hello"), the compiler infers that T = string from the argument type. Therefore the return type is string[] — a concrete type, not the generic T[]. This is the power of generics: type-safe reuse without losing specificity.
Challenge 4
Beginner
Optional Params
Solved
What does this function log when called with no argument?
TypeScript's optional parameter
? syntax. What happens when you call greet()?function greet(name?: string): string {
return `Hello, ${name ?? 'World'}!`;
}
console.log(greet());
Answer: B — "Hello, World!"
The
The
? makes name optional — its type is string | undefined. When omitted, it is undefined. Inside the function, name ?? 'World' uses nullish coalescing: undefined ?? 'World' evaluates to 'World'. So the output is "Hello, World!". No compile error — optional parameters are designed to be called with or without the argument.
Challenge 5
Intermediate
Type Narrowing
Solved
What is the type of value inside the if-block?
TypeScript narrows the type based on conditions. Inside the
if, what does TypeScript know about value?function process(value: string | number) {
if (typeof value === 'string') {
// What is the type of value here?
console.log(value.toUpperCase());
}
}
Answer: B — string
This is type narrowing. TypeScript uses control flow analysis to understand that inside the
This is type narrowing. TypeScript uses control flow analysis to understand that inside the
if (typeof value === 'string') block, the value can only be a string. The type is narrowed from string | number to just string. That's why calling .toUpperCase() (a string method) is valid — the compiler knows it's safe.
Challenge 6
Beginner
Interface vs Type
Solved
What is a key difference between interface and type?
Both
interface and type can describe object shapes, but they differ in one important way.interface Animal { name: string; }
interface Animal { legs: number; } // declaration merging
type Vehicle = { brand: string; };
type Vehicle = { speed: number; }; // ???
Answer: B — Only interface supports declaration merging; duplicate type alias is an error
Declaration merging: multiple
Declaration merging: multiple
interface declarations with the same name are automatically merged into one. The Animal interface ends up with both name and legs. This is useful for augmenting third-party types. type aliases cannot be merged — declaring type Vehicle twice is a compile error: Duplicate identifier 'Vehicle'.
Challenge 7
Intermediate
Readonly
Solved
What happens when you push to a readonly array?
TypeScript's
ReadonlyArray<T> (or readonly T[]) prevents mutation. What is the result of the push?const scores: readonly number[] = [10, 20, 30];
scores.push(40);
Answer: C — Compile-time error
readonly number[] (equivalent to ReadonlyArray<number>) removes all mutating methods like push, pop, splice, and direct index assignment. The TypeScript compiler reports: Property 'push' does not exist on type 'readonly number[]'. This is purely a compile-time check — the underlying JavaScript array is not frozen at runtime.
Challenge 8
Beginner
Enums
Solved
What is the value of Direction.Down?
Numeric enums auto-increment from their starting value. What value does
Direction.Down have?enum Direction {
Up = 10,
Down,
Left,
Right
}
console.log(Direction.Down);
Answer: D — 11
When a numeric enum member has an explicit value, subsequent members auto-increment from it.
When a numeric enum member has an explicit value, subsequent members auto-increment from it.
Up = 10, so Down = 11, Left = 12, Right = 13. If no starting value is given (e.g., enum Foo { A, B, C }), the first member defaults to 0 and each successive member increments by 1.
Challenge 9
Advanced
Intersection Types
Solved
What properties does Employee have?
An intersection type combines multiple types. What properties are required for a valid
Employee?type Person = { name: string; age: number };
type Worker = { company: string; role: string };
type Employee = Person & Worker;
const emp: Employee = {
// what goes here?
};
Answer: D — All four properties
An intersection type
An intersection type
A & B means the value must satisfy both types simultaneously. Employee requires all properties from Person (name, age) and all from Worker (company, role). Omitting any one property causes a compile error. This differs from a union A | B, which requires the value to satisfy just one of the types.
Challenge 10
Intermediate
as const
Solved
What is the inferred type of colors.primary?
The
as const assertion changes how TypeScript infers types. What is the type of colors.primary?const colors = {
primary: 'blue',
secondary: 'green'
} as const;
// What is the type of colors.primary?
Answer: B — "blue" (literal type)
Without
Without
as const, TypeScript widens the type of primary to string. With as const, the whole object is treated as deeply readonly and all values are narrowed to their literal types. So colors.primary has type "blue" (the specific string literal), not the general string type. This is essential for building type-safe lookup tables and discriminated unions.