Generic type variance in Typescript - Mini Project: Build & Apply
Notification<T> with a property message of type string and a property details of type T.Use interface Notification<T> and define message and details inside.
ReadonlyNotification<T> that makes the Notification<T> properties readonly.Use a mapped type with readonly and keyof to make properties readonly.
printNotification that takes a parameter notif of type ReadonlyNotification<T> and logs notif.message to the console.Define a generic function with parameter type ReadonlyNotification<T> and use console.log to print the message.
alertNotification of type Notification<{ level: string }> with message set to 'Warning!' and details set to { level: 'high' }. Then call printNotification with alertNotification.Define alertNotification with the exact type and values, then call printNotification(alertNotification).
Practice
Which of the following best describes covariance in TypeScript generic types?
Solution
Step 1: Understand covariance meaning
Covariance means you can use a more specific type (subtype) where a more general type (supertype) is expected.Step 2: Apply to generic types
In TypeScript, covariance allows substituting a subtype for a supertype safely in generics.Final Answer:
It allows a generic type to accept a subtype where a supertype is expected. -> Option BQuick Check:
Covariance = subtype for supertype [OK]
- Confusing covariance with contravariance
- Thinking covariance restricts substitution
- Assuming covariance means exact type only
Which of the following is the correct syntax to declare a generic function with contravariant parameter in TypeScript?
function process<T>(input: T): void { /* ... */ }Solution
Step 1: Recall TypeScript generic variance syntax
TypeScript does not useinoroutkeywords like some languages for variance.Step 2: Check valid generic function syntax
The correct syntax is simplyfunction process<T>(input: T): void { }without variance keywords.Final Answer:
function process<T>(input: T): void { } -> Option CQuick Check:
No variance keywords in TypeScript generics [OK]
- Using in/out keywords from other languages
- Trying to extend non-existent variance types
- Assuming variance is declared in generic syntax
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)?
Solution
Step 1: Understand ReadonlyArray variance
ReadonlyArray in TypeScript is covariant, so Dog[] can be used where ReadonlyArray<Animal> is expected.Step 2: Check function call validity
Passing dogs (Dog[]) to feedAnimals(animals: ReadonlyArray<Animal>) is allowed and compiles without error.Final Answer:
It compiles and runs without error because ReadonlyArray is covariant. -> Option DQuick Check:
ReadonlyArray is covariant = safe assignment [OK]
- Assuming arrays are invariant by default
- Confusing mutable arrays with ReadonlyArray
- Expecting runtime errors from type mismatch
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!') });Solution
Step 1: Analyze variance of Printer<T>
Printer<T> has a method accepting T as parameter, making it contravariant in T.Step 2: Check assignment direction
Assigning Printer<Animal> to Printer<Dog> is invalid because contravariance requires the opposite direction.Final Answer:
Assigning animalPrinter to dogPrinter causes a type error due to invariance. -> Option AQuick Check:
Function parameter generics are contravariant [OK]
- Ignoring contravariance in function parameters
- Assuming generic interfaces are always covariant
- Confusing parameter and return type variance
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?
Solution
Step 1: Understand contravariance in function parameters
Function parameters are contravariant, so a function type like(event: T) => voidmodels contravariance naturally.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.Step 3: Check invalid syntax options
interface EventHandler<in T> { handle(event: T): void; } usesinkeyword which TypeScript does not support; interface EventHandler<T> { handle(event: T): T; } changes return type, losing contravariance.Final Answer:
type EventHandler<T> = (event: T) => void; -> Option AQuick Check:
Function types are contravariant in parameters [OK]
- Using unsupported 'in' keyword in generics
- Assuming interfaces are contravariant by default
- Changing return type affects variance incorrectly
Ready to prove it?
Pass the test and this pattern moves unknown
