Bird
Raised Fist0
Typescriptprogramming~20 mins

Phantom types in Typescript - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Phantom Types Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ Predict Output
intermediate
2:00remaining
What is the output of this TypeScript phantom type example?
Consider this TypeScript code using phantom types to distinguish between different units of measurement. What will be logged to the console?
Typescript
type Meter = { readonly __brand: unique symbol };
type Second = { readonly __brand: unique symbol };

function speed(distance: number & Meter, time: number & Second): number {
  return distance / time;
}

const dist = 100 as number & Meter;
const t = 20 as number & Second;

console.log(speed(dist, t));
AType error at compile time
B20
CNaN
D5
Attempts:
2 left
💡 Hint
Phantom types don't affect runtime values but help TypeScript check types.
🧠 Conceptual
intermediate
1:30remaining
Why use phantom types in TypeScript?
Which of the following best explains the main purpose of phantom types in TypeScript?
ATo create new types without runtime overhead for better type safety
BTo add extra runtime checks for values
CTo enable dynamic type casting at runtime
DTo replace interfaces with classes
Attempts:
2 left
💡 Hint
Think about how phantom types affect the compiled JavaScript.
🔧 Debug
advanced
2:30remaining
Identify the error in this phantom type usage
This TypeScript code tries to use phantom types to prevent mixing currencies. What error will occur when compiling?
Typescript
type USD = { readonly __brand: unique symbol };
type EUR = { readonly __brand: unique symbol };

function addUSD(a: number & USD, b: number & USD): number & USD {
  return (a + b) as number & USD;
}

const priceUSD = 10 as number & USD;
const priceEUR = 15 as number & EUR;

const total = addUSD(priceUSD, priceEUR);
ANo error, total is 25
BRuntime error: Cannot add different currencies
CType error: Argument of type 'number & EUR' is not assignable to parameter of type 'number & USD'.
DSyntax error: Missing return type
Attempts:
2 left
💡 Hint
Check the types of arguments passed to addUSD.
📝 Syntax
advanced
2:00remaining
Which option correctly defines a phantom type in TypeScript?
Select the correct way to define a phantom type to distinguish between Kilograms and Pounds.
A
type Kilogram = number & { __brand: unique symbol };
type Pound = number & { __brand: unique symbol };
B
interface Kilogram { value: number; }
interface Pound { value: number; }
C
type Kilogram = number | { __brand: 'Kilogram' };
type Pound = number | { __brand: 'Pound' };
D
type Kilogram = number & { __brand: 'Kilogram' };
type Pound = number & { __brand: 'Pound' };
Attempts:
2 left
💡 Hint
Phantom types use intersection with unique symbols for branding.
🚀 Application
expert
3:00remaining
How many items are in the resulting map after using phantom types as keys?
Given this TypeScript code using phantom types as keys in a Map, how many entries does the map contain after execution?
Typescript
type UserId = string & { readonly __brand: unique symbol };

const user1 = 'abc123' as UserId;
const user2 = 'abc123' as UserId;

const map = new Map<UserId, string>();
map.set(user1, 'Alice');
map.set(user2, 'Bob');

console.log(map.size);
A2
B1
C0
DRuntime error
Attempts:
2 left
💡 Hint
Phantom types do not change the runtime value or identity of strings.

Practice

(1/5)
1. What is the main purpose of using phantom types in TypeScript?
easy
A. To improve performance by removing types
B. To create new objects at runtime
C. To add extra type information without affecting runtime data
D. To allow dynamic typing in TypeScript

Solution

  1. Step 1: Understand phantom types concept

    Phantom types add extra type information that exists only at compile time and does not affect the actual data or runtime behavior.
  2. Step 2: Compare options with concept

    Only To add extra type information without affecting runtime data correctly states that phantom types add extra type info without changing runtime data. Other options describe unrelated concepts.
  3. Final Answer:

    To add extra type information without affecting runtime data -> Option C
  4. Quick Check:

    Phantom types = extra compile-time info [OK]
Hint: Phantom types add info only at compile time, no runtime change [OK]
Common Mistakes:
  • Thinking phantom types create new objects
  • Confusing phantom types with runtime data changes
  • Believing phantom types improve runtime performance
2. Which of the following is the correct way to declare a phantom type in TypeScript?
easy
A. type UserId = string & { readonly __brand: unique symbol }
B. type UserId = string | number
C. type UserId = string[]
D. type UserId = number

Solution

  1. Step 1: Identify phantom type syntax

    Phantom types use intersection with a unique symbol or branded property to add phantom info without changing the base type.
  2. Step 2: Check each option

    type UserId = string & { readonly __brand: unique symbol } uses intersection with a unique symbol brand, which is the correct phantom type pattern. Others are normal types or unions.
  3. Final Answer:

    type UserId = string & { readonly __brand: unique symbol } -> Option A
  4. Quick Check:

    Phantom type = base type & unique brand [OK]
Hint: Phantom types use intersection with unique symbol branding [OK]
Common Mistakes:
  • Using union types instead of intersection for phantom types
  • Declaring phantom types as arrays or simple types
  • Missing the unique symbol branding
3. What will be the result of this TypeScript code?
type Meter = number & { readonly __unit: 'meter' };
function addDistance(a: Meter, b: Meter): Meter {
  return (a + b) as Meter;
}
const dist1 = 5 as Meter;
const dist2 = 10 as Meter;
const total = addDistance(dist1, dist2);
console.log(total);
medium
A. NaN
B. 15
C. Type error at addDistance call
D. undefined

Solution

  1. Step 1: Understand phantom type behavior at runtime

    Phantom types exist only at compile time. At runtime, they are just numbers. So adding two numbers works normally.
  2. Step 2: Analyze code output

    dist1 and dist2 are numbers with phantom type. Adding them returns 15, which is logged.
  3. Final Answer:

    15 -> Option B
  4. Quick Check:

    Phantom types don't affect runtime value [OK]
Hint: Phantom types vanish at runtime; values behave normally [OK]
Common Mistakes:
  • Expecting type errors at runtime
  • Thinking phantom types change runtime values
  • Confusing phantom types with runtime wrappers
4. Identify the error in this phantom type usage:
type UserId = string & { readonly __brand: unique symbol };
function getUser(id: UserId) {
  return `User: ${id}`;
}
const id = 'abc123';
getUser(id);
medium
A. Function getUser should return a number, not string
B. No error, code runs fine
C. Unique symbol cannot be used in type intersections
D. Passing a plain string instead of branded UserId causes a type error

Solution

  1. Step 1: Check argument type for getUser

    getUser expects a UserId (string with phantom brand). Passing plain string 'abc123' lacks the brand, causing a type error.
  2. Step 2: Validate other options

    Return type is correct, unique symbol usage is valid, and code does not run fine due to type mismatch.
  3. Final Answer:

    Passing a plain string instead of branded UserId causes a type error -> Option D
  4. Quick Check:

    Phantom type mismatch = type error [OK]
Hint: Phantom types require branded values, not plain base types [OK]
Common Mistakes:
  • Ignoring phantom brand when passing values
  • Thinking unique symbol is invalid in types
  • Assuming no error when types differ
5. How can phantom types help prevent mixing different ID types in a TypeScript project?
Consider these types:
type UserId = string & { readonly __brand: 'UserId' };
type ProductId = string & { readonly __brand: 'ProductId' };

What is the main benefit of this approach?
hard
A. It prevents accidentally using a ProductId where a UserId is expected at compile time
B. It improves runtime performance by optimizing string storage
C. It automatically converts between UserId and ProductId
D. It allows mixing IDs freely without errors

Solution

  1. Step 1: Understand phantom branding for IDs

    Phantom types add unique brands to similar base types (string) to distinguish them at compile time.
  2. Step 2: Identify benefit in type safety

    This branding prevents passing a ProductId where a UserId is expected, catching errors before running code.
  3. Final Answer:

    It prevents accidentally using a ProductId where a UserId is expected at compile time -> Option A
  4. Quick Check:

    Phantom types enforce distinct types safely [OK]
Hint: Phantom types catch ID mix-ups at compile time [OK]
Common Mistakes:
  • Thinking phantom types affect runtime performance
  • Assuming automatic conversion between branded types
  • Believing phantom types allow mixing without errors
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard