The Basics Flashcards
Declare a let called gen for a generic type called GenericType that takes 2 Specific types: SpecificType1 and SpecificType2
let gen : GenericType<SpecificType1, SpecificType2, ...>;
Create a let called coordinates and assign it to an array of coordinate using this type:
type Coordinate = [number, number];
let coordinates: Array<Coordinate>;
Will this yield an error:
type Coordinate = [number, number]; let coordinates: Array<Coordinate>; coordinates = [ [30, 100, 0], [100, 50], ];
Yes, because each coordinate can only have 2 dimensions.
Create a let called result that is a generic promise to return something of type Response.
If you don’t know the Type Response see:
https://developer.mozilla.org/en-US/docs/Web/API/Response
let result: Promise<Response>;
This is useful when fetching information from api like so:
let promisedResponse: Promise<Response> = fetch("https://myapi.dev/api/");
How can we use the result of this to output if the response was OK?
const promisedResponse: Promise<Response> = fetch("https://swapi.dev/api/");
promisedResponse.then((res) => console.log(res.ok));
How can we narrow the type of the record keys so that only ‘rodj’ or ‘janes’ are accepted?
type Result = { firstName: string; surname: string; score: number; }; type ResultRecord = Record<string, Result>; const records: ResultRecord = { rodj: { firstName: "Rod", surname: "James", score: 70, }, janes: { firstName: "Jane", surname: "Smith", score: 95, } };
By changing the ResultRecord type definition from:
type ResultRecord = Record<string, Result>;
to:
type ResultRecord = Record<"rodj"|"janes", Result>;
Make this function generic:
function firstOrNull(array: string[]): string | null { return array.length === 0 ? null : array[0]; }
function firstOrNull<T>(array: T[]): T | null { return array.length === 0 ? null : array[0]; }
Declare an array and bind it to a variable called myArray for the class Product without using:
new Array aka Array<Product>:
class Product { name: string; desc: string; meta: string }
let myArray : Product[];
Note: if you use const you have to initialize the array like so:
const myArray: Product[] = [];
Can interfaces be generic and accept multiples types ?
Yes. A common use if for forms.
Create a typed const contactForm using these 2 interfaces and assign a first value of “bob” and “bob@someemail.com” to contactForm:
interface Form<T> { values: T; } interface Contact { name: string; email: string; }
const contactForm: Form<Contact> = { values: { name: "Bob", email: "bob@someemail.com" } }
Explain this interface and what the K in keyof T is…
interface Form<T> { values: T; errors: { [K in keyof T]?: string; }; }
- The type is in curly brackets, so we are constructing an object type.
- [K in keyof T] will put all the keys in the type T into a string literal union. This will be “name” | “email” for contactForm.
- [K in keyof T] is the property name of the object being constructed (T).
- The ? after the property name means the properties are optional.
- The type for the properties is string.
So given a T that would be an interface:
interface Contact { name: string; email: string; }
The type for the errors is:
{name?: string; email?: string}.
Is it possible to pass types into a type alias ?
Yes. Using this syntax:
type TypeName<T1, T2, ...> = { ... }
Implement a generic Form type with 2 properties:
values for our generic T
and an
optional errors for each key of T
type Form<T> = { values: T; errors: { [K in keyof T]?: string }; };
Can we declare generic classes ?
Yes
What is the syntax to create a generic class using T1,T2,T3..
class ClassName<T1, T2, …> {
…
}
Create a generic class called List that will take a single generic type T and keep the items in a private array of T called items.
class List<T> { private items: T[] = []; }
Add a add method on this class to add an item of type T.
This new add method needs to push a provided item of type T in private Items.
class List<T> { private items: T[] = []; }
class List<T> { private items: T[] = []; add(item: T){ this.items.push(item); } }
Can generic parameter have a default value?
Yes
How do we specify a default on a generic type T ? Provide an example where the type is called TypeName the generic type if T and the default type of T is DefaultType.
type TypeName<T= DefaultType> ={ ... }
Will this raise any error?
interface Component<T1 = string, T2 = any> { name: T1; props: T2; log: () => void; } const button: Component = { name: "Button", props: { text: "Save", }, log: () => console.log("Save button"), }; console.log(button.props.text); console.log(button.props.text2);
No. Because absolutely no type checking is done on T2 since it’s any.
Note: When a generic parameter default is used, the type might be wider than necessary.
Will this yield an error:
function firstOrNull<T>(array: T[]): T | null { return array.length === 0 ? null : array[0]; } const first = firstOrNull([1, 2, 3]); console.log(first);
No. Even if we didn’t supply the generic parameter to the function. Generic parameters aren’t required on functions. In this case, the parameter has been inferred to be number.
Supply a default type of string for T in this function:
function firstOrNull<T>(array: T[]): T | null { return array.length === 0 ? null : array[0]; }
function firstOrNull<T = string>(array: T[]): T | null { ... }
Given this code:
function firstOrNull<T = string>(array: T[]): T | null { return array.length === 0 ? null : array[0]; } const first = firstOrNull([1, 2, 3]); console.log(first);
What will be the type of first?
first has been inferred to be number. The default generic parameter type was overridden by the type inference of the function parameters.
Is it possible to require generic parameters to have a particular structure?
Yes
How do we put a constraint of ConstrainingType on a generic parameter T?
<T extends ContrainingType>
Will this raise an error? If so why?
interface Logable { log: () => void; } function logItems<T>(items: T[]): void { items.forEach(item => item.log()); }
Yes. TypeScript doesn’t know that the array items contain a log method, so, it raises an error.
This function will raise an error since Typescript doesn’t know that the item implement .log(). Fix this by adding a parameter constraints on the generic type.
interface Logable { log: () => void; } function logItems<T>(items: T[]): void { items.forEach(item => item.log()); }
interface Logable { log: () => void; } function logItems<T extends Logable>(items: T[]): void { items.forEach(item => item.log()); }
Can a generic parameter constraint be dependent on another generic parameter?
Yes
This raise an error. Why?
interface Form<T> { values: T; } function getFieldValue<T>(form: Form<T>, fieldName: string) { return form.values[fieldName]; }
TypeScript doesn’t know what the structure of value is, so, it raises an error.
This raise an error:
interface Form<T> { values: T; } function getFieldValue<T>(form: Form<T>, fieldName: string) { return form.values[fieldName]; }
Fix it using parameter constraint.
interface Form<T> { values: T; } function getFieldValue<T, K extends keyof T>(form: Form<T>, fieldName: K) { return form.values[fieldName]; }
Note: The TypeScript keyof keyword queries the keys of the type referenced after it.
What is the rest element?
A rest element type is a type for a collection of tuple elements.
Is it possible to use generic rest elements?
Yes. For example:type NameAndThings<T extends unknown[]> = [string, ...T];
Add labels to make the structure of the tuple a little more readable. Add a label name and things to this code:
type NameAndThings<T extends unknown[]> = [string, ...T];
type NameAndThings<T extends unknown[]> = [name: string, ...things: T];
Using this type:type NameAndThings<T extends unknown[]> = [name: string, ...things: T];
Declare a let variable named scores to hold the following numeric scores for bob: 4,5 and pass in bob’s name.
let scores: NameAndThings<number[]>; scores = ["Bob", 4, 9, 3];
Using this function declare a variable to hold grades scores for Bill using our NameAndThings type. The tuple should contain a string-based name in the first element and grades in subsequent elements. The grades can only be ‘A’, ‘B’, or ‘C’.
type NameAndThings<T extends unknown[]> = [name:string, ...things:T];
type NameAndThings<T extends unknown[]> = [name:string, ...things:T]; type Grade = "a"|"b"|"c"; let billGrades : NameAndThings<Grade[]>; billGrades = ["bill","a","b","c"] ///or let billGrades: NameAndThings<("A" | "B" | "C")[]>;
Will this raise an error:
function logThings<T extends unknown[]>(name: string, ...things: T) { console.log(things); } logThings("Bob", 4, 9, 3);
No. TypeScript cleverly infers the generic parameter type from the parameter values.
Will this raise an error:
function logThings<T extends unknown[]>(name: string, ...things: T) { console.log(things); } logThings("Bob", 4, "9", 3);
No, TypeScript infers the generic parameter type to be [number, string, number].
Will this raise an error:
function logThings<T extends unknown[]>(name: string, ...things: T) { console.log(things); } logThings<number[]>("Bob", 4, "9", 3);
Yes, because ‘9’ is not a number type.
Can function parameter be thought of as a tuple?
Yes. Typing function parameters as a tuple allow strongly-typed functions to be created that have varying parameters.
What will be the type of scores in:
function merge(names: string[], scores: number[]) { return [...names, ...scores]; } let scores = merge(["Bill", "Jane"], [8, 9]);
(string | number)[]
What are Names and Scores in this function:
function merge<Names extends string[], Scores extends number[]>( names: Names, scores: Scores ) { return [...names, ...scores]; }
Generic parameter types
What is the type of scores here:
function merge<Names extends string[], Scores extends number[]>( names: Names, scores: Scores ) { return [...names, ...scores]; } let scores = merge(["Bill", "Jane"], [8, 9]);
(string | number)[]
What is the type of scores here:
function merge<Names extends string[], Scores extends number[]>( names: [...Names], scores: [...Scores] ) { return [...names, ...scores]; } let scores = merge(["Bill", "Jane"], [8, 9]);
(‘Bill’ | ‘Jane’ | 8 | 9)[]
We are using the spread operator here to specify the return type. What will be the type of scores:
function merge<Names extends string[], Scores extends number[]>( names: [...Names], scores: [...Scores] ): [...Names, ...Scores] { return [...names, ...scores]; } let scores = merge(["Bill", "Jane"], [8, 9]);
[‘Bill’, ‘Jane’, 8, 9]
We have a function below which outputs the name property of an object to the console. How can we use generics to make this more strongly-typed?
function logName(object: any) { console.log("My name is " + object.name); }
function logName<T extends {name: string}>(object: T) {
console.log(“My name is “ + object.name);
}
What is the syntax to add T1, T2 to a type of TypeName?
type TypeName<T1, T2, ...> = { ... }