TS03 Exercises
Practical Exercises
- Write a generic function reverseArray that takes an array of any type and returns a new array with the elements in reverse order. Use appropriate generic types.
function reverseArray<T>(array: T[]): T[] {
return array.slice().reverse();
}- Create a generic function swapTuple that takes a tuple containing two elements and returns a new tuple where the elements are swapped. Use appropriate generic types.
function swapTuple<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}- Write a generic function getValue that takes an object and a key, and returns the value of the key in that object. Use keyof to ensure the key exists in the object.
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}- Write a generic function named
filterMapthat takes aMap<K, V>and a callback and returns all keys in the map, for whichcallback(value)returns true (similar toArray.filter).
function filterMap<K, V>(map: Map<K, V>, callback: (value: V) => boolean): K[] {
const result: K[] = [];
map.forEach((value, key) => {
if (callback(value)) {
result.push(key);
}
});
return result;
}- Create a generic function named
unionSetsthat takes twoSet<T>objects and returns a new set that is the union of these sets.
function unionSets<T>(setA: Set<T>, setB: Set<T>): Set<T> {
return new Set([...setA, ...setB]);
}- Create a generic function named mapToRecord that takes a
Map<K, V>and returns aRecord<K, V>with the same key-value pairs.
function mapToRecord<K extends string | number | symbol, V>(map: Map<K, V>): Record<K, V> {
const record: Record<K, V> = {} as Record<K, V>;
map.forEach((value, key) => {
record[key] = value;
});
return record;
}- Write a generic function named merge that takes two
Record<K, V>objects and returns a new record combining both, with the second one's values overriding the first one's if there are any conflicts.
function merge<K extends string | number | symbol, V>(record1: Record<K, V>, record2: Record<K, V>): Record<K, V> {
return { ...record1, ...record2 };
}Theoretical Questions
- Explain what a generic function is in TypeScript. How do type variables in generic functions improve code reusability and type safety?
A generic function in TypeScript allows you to write a function that can work with any data type without losing the information about that type. Type variables in generic functions enable this flexibility, allowing the function to handle different types while maintaining type safety. This improves code reusability because the same function can work with different types without redundancy.
- Describe type argument inference in the context of generic functions. How does TypeScript infer the type of generic arguments when a function is called?
Type argument inference in TypeScript occurs when the compiler automatically determines the type arguments based on the actual arguments provided to a function. This means that when you call a generic function, TypeScript tries to infer the types from the values passed to the function, eliminating the need to explicitly define the type in many cases.
- What does it mean to 'push type parameters down' in generic functions, and why is it considered a good practice? Why is it recommended to use fewer type parameters in generic functions?
- 'Pushing type parameters down' means using generics at the lowest possible level, such as on a single function or method rather than an entire class. This approach increases the reusability and flexibility of the function.
- Using fewer type parameters is recommended because it makes the code simpler and easier to understand. Overusing generic types can lead to overly complex and hard-to-maintain code.
- Explain the keyof type operator in TypeScript and its use in generic functions. Provide an example where keyof is particularly useful.
The keyof type operator in TypeScript is used to create a union type of all the known,
public property names of a type. It is useful in generic functions to ensure that the keys passed as
arguments actually exist on the object. For example, in a function that accesses properties of an object,
using keyof ensures that only valid property names for that object can be used.
- Describe the typeof type operator and its role in TypeScript. How is typeof used differently in TypeScript compared to JavaScript?
The typeof operator in TypeScript is used to capture the type of a variable. Unlike JavaScript, where
typeof returns a string representing the type, in TypeScript, it is used in type contexts to refer
to the type of a variable or property. This can be useful for defining types based on existing variables.
- What are generic constraints in TypeScript? Give an example of a scenario where you would use generic constraints.
Generic constraints in TypeScript allow you to specify that a type parameter must meet certain criteria. This is useful when you want to limit the types that can be used with a generic function or type. For instance, you might constrain a type parameter to be a type that has a certain method or extends a certain class.
- Explain the significance of the following generic types in TypeScript and provide examples of their use:
Array<T>Map<K, V>Set<T>Promise<T>Record<K, V>
- Discuss how generics are used with collections like Array, Map, and Set. How do generics enhance these data structures?
In TypeScript, generics enhance collections by allowing them to work with various types while
maintaining type safety. For example, a Map<K, V> can store keys and values of any specified types,
and operations on the map will be type-checked