Bird
Raised Fist0
Typescriptprogramming~20 mins

Generic type variance in Typescript - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Generic Type Variance Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ Predict Output
intermediate
2:00remaining
Output of generic function with covariance
What is the output of this TypeScript code when run with console.log(result);?
Typescript
interface Animal { name: string }
interface Dog extends Animal { bark(): void }

function getName<T extends Animal>(animal: T): string {
  return animal.name;
}

const dog: Dog = { name: "Buddy", bark: () => console.log("Woof!") };
const result = getName(dog);
A"Buddy"
Bundefined
CTypeError at runtime
DCompilation error
Attempts:
2 left
💡 Hint
Think about how the generic type T extends Animal and what property is accessed.
❓ Predict Output
intermediate
2:00remaining
Effect of contravariance in function parameters
What will this TypeScript code output when executed?
Typescript
interface Animal { name: string }
interface Dog extends Animal { bark(): void }

function feedAnimal(animal: Animal) {
  return `Feeding ${animal.name}`;
}

const feedDog: (dog: Dog) => string = feedAnimal;
const dog: Dog = { name: "Max", bark: () => console.log("Woof!") };
const result = feedDog(dog);
console.log(result);
A"Feeding Max"
BTypeError at runtime
CCompilation error due to type incompatibility
Dundefined
Attempts:
2 left
💡 Hint
Consider function parameter type compatibility and contravariance.
🔧 Debug
advanced
2:30remaining
Identify the variance error in generic interface assignment
Which option correctly explains the error in this TypeScript code snippet?
Typescript
interface Box<T> {
  value: T;
}

let animalBox: Box<Animal> = { value: { name: "Leo" } };
let dogBox: Box<Dog> = animalBox;
AError because Dog is not assignable to Animal
BNo error, assignment is valid due to covariance
CError because Box<T> is invariant and Box<Animal> cannot be assigned to Box<Dog>
DError because Box<T> is contravariant
Attempts:
2 left
💡 Hint
Think about how generic types behave when assigned to each other.
📝 Syntax
advanced
2:00remaining
Which generic function signature is valid for contravariant parameter?
Which option shows a valid TypeScript generic function signature that enforces contravariance on the parameter type?
Afunction process<T>(input: T): void
Bfunction process<in T>(input: T): void
Cfunction process<T super Animal>(input: T): void
Dfunction process<T extends Animal>(input: T): void
Attempts:
2 left
💡 Hint
TypeScript does not support 'in' or 'super' keywords for generics like some other languages.
🚀 Application
expert
3:00remaining
Determine the output of complex generic variance with function types
Given the following TypeScript code, what is the output when console.log(result); runs?
Typescript
interface Animal { name: string }
interface Dog extends Animal { bark(): void }

function handler1(animal: Animal): string {
  return `Animal: ${animal.name}`;
}

function handler2(dog: Dog): string {
  return `Dog: ${dog.name}`;
}

let func1: (dog: Dog) => string = handler1;
let dog: Dog = { name: "Rex", bark: () => console.log("Woof!") };
let result = func1(dog);
ATypeError at runtime
B"Animal: Rex"
CCompilation error due to incompatible function types
D"Dog: Rex"
Attempts:
2 left
💡 Hint
Consider function parameter contravariance and return type covariance in TypeScript.

Practice

(1/5)
1.

Which of the following best describes covariance in TypeScript generic types?

easy
A. It allows a generic type to accept a supertype where a subtype is expected.
B. It allows a generic type to accept a subtype where a supertype is expected.
C. It restricts generic types to only accept exact types without substitution.
D. It means generic types cannot be assigned to each other regardless of relation.

Solution

  1. Step 1: Understand covariance meaning

    Covariance means you can use a more specific type (subtype) where a more general type (supertype) is expected.
  2. Step 2: Apply to generic types

    In TypeScript, covariance allows substituting a subtype for a supertype safely in generics.
  3. Final Answer:

    It allows a generic type to accept a subtype where a supertype is expected. -> Option B
  4. Quick Check:

    Covariance = subtype for supertype [OK]
Hint: Covariance means subtype can replace supertype [OK]
Common Mistakes:
  • Confusing covariance with contravariance
  • Thinking covariance restricts substitution
  • Assuming covariance means exact type only
2.

Which of the following is the correct syntax to declare a generic function with contravariant parameter in TypeScript?

function process<T>(input: T): void { /* ... */ }
easy
A. function process(input: T): void { }
B. function process(input: T): void { }
C. function process<T>(input: T): void { }
D. function process(input: T): void { }

Solution

  1. Step 1: Recall TypeScript generic variance syntax

    TypeScript does not use in or out keywords like some languages for variance.
  2. Step 2: Check valid generic function syntax

    The correct syntax is simply function process<T>(input: T): void { } without variance keywords.
  3. Final Answer:

    function process<T>(input: T): void { } -> Option C
  4. Quick Check:

    No variance keywords in TypeScript generics [OK]
Hint: TypeScript uses no in/out keywords for generics [OK]
Common Mistakes:
  • Using in/out keywords from other languages
  • Trying to extend non-existent variance types
  • Assuming variance is declared in generic syntax
3.

Consider the following TypeScript code:

interface Animal { name: string; }
interface Dog extends Animal { bark(): void; }

function feedAnimals(animals: ReadonlyArray<Animal>) { /* ... */ }

const dogs: Dog[] = [{ name: 'Rex', bark: () => console.log('Woof!') }];
feedAnimals(dogs);

What is the output or result of calling feedAnimals(dogs)?

medium
A. It causes a compile-time error due to type incompatibility.
B. It compiles but silently ignores the dogs array.
C. It runs but throws a runtime error because of type mismatch.
D. It compiles and runs without error because ReadonlyArray is covariant.

Solution

  1. Step 1: Understand ReadonlyArray variance

    ReadonlyArray in TypeScript is covariant, so Dog[] can be used where ReadonlyArray<Animal> is expected.
  2. Step 2: Check function call validity

    Passing dogs (Dog[]) to feedAnimals(animals: ReadonlyArray<Animal>) is allowed and compiles without error.
  3. Final Answer:

    It compiles and runs without error because ReadonlyArray is covariant. -> Option D
  4. Quick Check:

    ReadonlyArray is covariant = safe assignment [OK]
Hint: ReadonlyArray is covariant, so subtype arrays pass [OK]
Common Mistakes:
  • Assuming arrays are invariant by default
  • Confusing mutable arrays with ReadonlyArray
  • Expecting runtime errors from type mismatch
4.

Identify the error in the following TypeScript code related to generic variance:

interface Printer<T> {
  print(value: T): void;
}

const animalPrinter: Printer<Animal> = {
  print: (animal: Animal) => console.log(animal.name)
};

const dogPrinter: Printer<Dog> = animalPrinter;
dogPrinter.print({ name: 'Buddy', bark: () => console.log('Woof!') });
medium
A. Assigning animalPrinter to dogPrinter causes a type error due to invariance.
B. The print method signature is incorrect and causes a syntax error.
C. There is no error; the code runs and prints 'Buddy'.
D. The dogPrinter variable is not declared properly.

Solution

  1. Step 1: Analyze variance of Printer<T>

    Printer<T> has a method accepting T as parameter, making it contravariant in T.
  2. Step 2: Check assignment direction

    Assigning Printer<Animal> to Printer<Dog> is invalid because contravariance requires the opposite direction.
  3. Final Answer:

    Assigning animalPrinter to dogPrinter causes a type error due to invariance. -> Option A
  4. Quick Check:

    Function parameter generics are contravariant [OK]
Hint: Function parameters are contravariant; assignment direction matters [OK]
Common Mistakes:
  • Ignoring contravariance in function parameters
  • Assuming generic interfaces are always covariant
  • Confusing parameter and return type variance
5.

You want to create a generic event handler interface in TypeScript that accepts events of type T. The handler should be able to accept handlers for supertypes of T (contravariance). Which of the following interface declarations correctly models this behavior?

hard
A. type EventHandler<T> = (event: T) => void;
B. interface EventHandler<in T> { handle(event: T): void; }
C. interface EventHandler<T> { handle(event: T): T; }
D. interface EventHandler<T> { handle(event: T): void; }

Solution

  1. Step 1: Understand contravariance in function parameters

    Function parameters are contravariant, so a function type like (event: T) => void models contravariance naturally.
  2. Step 2: Compare interface and type alias

    Interfaces with methods are invariant by default in TypeScript, but function types are contravariant in parameters, so using a type alias for a function is correct.
  3. Step 3: Check invalid syntax options

    interface EventHandler<in T> { handle(event: T): void; } uses in keyword which TypeScript does not support; interface EventHandler<T> { handle(event: T): T; } changes return type, losing contravariance.
  4. Final Answer:

    type EventHandler<T> = (event: T) => void; -> Option A
  5. Quick Check:

    Function types are contravariant in parameters [OK]
Hint: Use function types for contravariant parameters [OK]
Common Mistakes:
  • Using unsupported 'in' keyword in generics
  • Assuming interfaces are contravariant by default
  • Changing return type affects variance incorrectly
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