Bird
Raised Fist0
Typescriptprogramming~20 mins

Why type narrowing is needed in Typescript - Challenge Your Understanding

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Type Narrowing 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 code with type narrowing?

Look at the code below. What will it print when run?

Typescript
function printLength(x: string | number) {
  if (typeof x === 'string') {
    console.log(x.length);
  } else {
    console.log(x.toFixed(2));
  }
}
printLength('hello');
printLength(3.14159);
A5 and 3.14
Bundefined and 3.14
C5 and 3.14159
DError at compile time
Attempts:
2 left
💡 Hint

Think about how TypeScript knows what methods are safe to call on x.

🧠 Conceptual
intermediate
1:30remaining
Why do we need type narrowing in TypeScript?

Choose the best reason why type narrowing is important in TypeScript.

ATo allow calling properties or methods safely on union types by knowing the exact type at runtime
BTo make the code run faster by removing type checks
CTo convert all types to strings automatically
DTo disable type checking for certain variables
Attempts:
2 left
💡 Hint

Think about how TypeScript prevents errors when a variable can be more than one type.

🔧 Debug
advanced
1:30remaining
What error does this TypeScript code raise without type narrowing?

Look at this code. What error will TypeScript show?

Typescript
function doubleValue(x: string | number) {
  return x * 2;
}
AError: Missing return statement
BNo error, returns doubled value for both types
CError: Operator '*' cannot be applied to types 'string | number' and 'number'.
DError: Cannot multiply by 2
Attempts:
2 left
💡 Hint

Think about what happens when you try to multiply a string by a number in TypeScript.

📝 Syntax
advanced
2:00remaining
Which option correctly narrows the type to string in this code?

Choose the correct way to narrow the type of value to string before calling toUpperCase().

Typescript
function shout(value: string | number) {
  // Narrow value to string here
  console.log(value.toUpperCase());
}
Aif (value instanceof String) { console.log(value.toUpperCase()); }
Bif (value === 'string') { console.log(value.toUpperCase()); }
Cif (typeof value === 'number') { console.log(value.toUpperCase()); }
Dif (typeof value === 'string') { console.log(value.toUpperCase()); }
Attempts:
2 left
💡 Hint

Remember how to check the type of a variable in JavaScript/TypeScript.

🚀 Application
expert
2:30remaining
What is the output of this TypeScript code using type narrowing with custom type guard?

Consider this code with a custom type guard function. What will it print?

Typescript
type Fish = { swim: () => void };
type Bird = { fly: () => void };

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    return 'Fish swims';
  } else {
    return 'Bird flies';
  }
}

console.log(move({ swim: () => {} }));
console.log(move({ fly: () => {} }));
A"Bird flies" followed by "Fish swims"
B"Fish swims" followed by "Bird flies"
CRuntime error because of wrong type assertion
DBoth calls return "Fish swims"
Attempts:
2 left
💡 Hint

Look at how the custom type guard works and what it checks.

Practice

(1/5)
1. Why do we need type narrowing in TypeScript when a variable can have multiple types?
easy
A. To tell TypeScript the exact type so it can check code safety
B. To make the code run faster at runtime
C. To allow variables to change types dynamically
D. To avoid writing any type annotations

Solution

  1. Step 1: Understand the role of type narrowing

    Type narrowing helps TypeScript know the exact type of a variable at a certain point in code.
  2. Step 2: Recognize the benefit for code safety

    Knowing the exact type allows TypeScript to check for errors and prevent bugs before running the code.
  3. Final Answer:

    To tell TypeScript the exact type so it can check code safety -> Option A
  4. Quick Check:

    Type narrowing = safer code [OK]
Hint: Type narrowing means telling TypeScript exact types [OK]
Common Mistakes:
  • Thinking it speeds up runtime
  • Believing it allows dynamic type changes
  • Assuming it removes need for type annotations
2. Which of the following is a correct way to narrow a variable's type in TypeScript?
easy
A. function test(value: number | string) { return value; }
B. value = 'string' as number;
C. let value: any = 5; value = 'hello';
D. if (typeof value === 'string') { /* value is string here */ }

Solution

  1. Step 1: Identify type narrowing syntax

    Using typeof checks like typeof value === 'string' narrows the type inside the block.
  2. Step 2: Check other options for correctness

    value = 'string' as number; forces wrong type with as, let value: any = 5; value = 'hello'; uses any without narrowing, function test(value: number | string) { return value; } does not narrow inside function.
  3. Final Answer:

    if (typeof value === 'string') { /* value is string here */ } -> Option D
  4. Quick Check:

    typeof check narrows type [OK]
Hint: Use typeof or instanceof to narrow types [OK]
Common Mistakes:
  • Using 'as' to force wrong types
  • Not using any type checks
  • Confusing type assertion with narrowing
3. What will be the output of this TypeScript code?
function printLength(x: string | number) {
  if (typeof x === 'string') {
    return x.length;
  } else {
    return x.toFixed(2);
  }
}
console.log(printLength('hello'));
console.log(printLength(3.14159));
medium
A. hello and 3.14
B. 5 and 3.14159
C. 5 and 3.14
D. Error because length is not on number

Solution

  1. Step 1: Analyze the type narrowing in the function

    The if checks if x is a string, then returns its length (5 for 'hello'). Else, it treats x as number and calls toFixed(2).
  2. Step 2: Evaluate the outputs for given inputs

    For 'hello', output is 5. For 3.14159, toFixed(2) returns '3.14'.
  3. Final Answer:

    5 and 3.14 -> Option C
  4. Quick Check:

    Type narrowing enables correct method calls [OK]
Hint: Check typeof to know which method runs [OK]
Common Mistakes:
  • Expecting full decimal for number output
  • Assuming length works on number
  • Ignoring type narrowing effect
4. Identify the error in this TypeScript code and why type narrowing is needed:
function process(value: string | number) {
  console.log(value.length);
}
medium
A. Error: string type cannot be logged
B. Error: number type has no 'length' property, need narrowing
C. No error, code runs fine
D. Error: function missing return type

Solution

  1. Step 1: Understand the union type usage

    The parameter value can be string or number, but length exists only on string.
  2. Step 2: Identify why narrowing is needed

    Without checking type, accessing length on number causes error. Type narrowing is needed to safely access length.
  3. Final Answer:

    Error: number type has no 'length' property, need narrowing -> Option B
  4. Quick Check:

    Accessing properties needs type narrowing [OK]
Hint: Check type before accessing properties [OK]
Common Mistakes:
  • Ignoring union types
  • Assuming all types have same properties
  • Not using if or typeof checks
5. You have a function that accepts string | number | null. You want to safely return the string length or number fixed to 1 decimal, or 0 if null. Which code correctly uses type narrowing?
hard
A. function format(x: string | number | null) { if (x === null) return 0; if (typeof x === 'string') return x.length; return x.toFixed(1); }
B. function format(x: string | number | null) { if (x) return x.length || x.toFixed(1); return 0; }
C. function format(x: string | number | null) { return x.length ?? x.toFixed(1) ?? 0; }
D. function format(x: string | number | null) { if (typeof x === 'number') return x.length; else if (typeof x === 'string') return x.toFixed(1); else return 0; }

Solution

  1. Step 1: Check handling of null

    function format(x: string | number | null) { if (x === null) return 0; if (typeof x === 'string') return x.length; return x.toFixed(1); } explicitly returns 0 if x is null, which is correct.
  2. Step 2: Verify type narrowing for string and number

    function format(x: string | number | null) { if (x === null) return 0; if (typeof x === 'string') return x.length; return x.toFixed(1); } uses typeof to check string and number separately, calling correct methods safely.
  3. Step 3: Identify errors in other options

    function format(x: string | number | null) { if (x) return x.length || x.toFixed(1); return 0; } incorrectly uses x.length || x.toFixed(1) without narrowing; function format(x: string | number | null) { return x.length ?? x.toFixed(1) ?? 0; } uses nullish coalescing incorrectly; function format(x: string | number | null) { if (typeof x === 'number') return x.length; else if (typeof x === 'string') return x.toFixed(1); else return 0; } swaps methods for types.
  4. Final Answer:

    function format(x: string | number | null) { if (x === null) return 0; if (typeof x === 'string') return x.length; return x.toFixed(1); } -> Option A
  5. Quick Check:

    Explicit checks safely narrow types [OK]
Hint: Check null first, then typeof string or number [OK]
Common Mistakes:
  • Not checking null before accessing properties
  • Mixing up string and number methods
  • Using || or ?? without narrowing
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