The Basics Flashcards
What are mapped types?
Mapped types allow us to create new types from existing types.
We can think of a mapped type as the array map function, but it maps types rather than values.
What is the syntax to use the keyof operator? What does it do?
let keys: keyof ExistingType;
The keyof operator to extract the keys from an object in a type annotation. When TypeScript sees the keyof operator in a type annotation, it queries the type after it and extracts all its keys. It then constructs a union string literal type from the keys.
Note: The keyof operator is sometimes referred to as the index query operator because it queries the type specified after it.
What are people referring to when they talk about the the index query operator?
The keyof operator.
Use the keyof operator to extract the keys of this type to a let variable called keys:type ContactDetails = { name: string; email: string };
let keys : keyof ContactDetails;
What is the type of keys here:
~~~
type ContactDetails = { name: string; email: string };
let keys : keyof ContactDetails;
~~~
The type of keys is ‘name’ | ‘email’. Note that in recent versions of TypeScript, when you hover over keys, the type will be printed as keyof ContactDetails.
What is a mapped type?
A mapped type is the process of creating a new type by mapping type information from an existing type.type MappedTypeName = { [K in UnionType]: ExistingType };
What is this code for and explain what it does:
type NewTypeName = { [K in UnionType]: ExistingType };
This is a MappedTypeName.
The in operator maps over each item in the union type to create a new type. In other words, the in operator allows us to loop through each type in a union type. In a loop iteration, wach item in the union type is put in K, which becomes a key in the new type. So, the union type is usually a union of string literals. The type annotation in the mapped type is the type given to each key in the type.
What will be the output of the type being created here:type ContactDetails = { [K in "name" | "email"]: string };
{ name: string; email: string; }
Is there an error in this code? Update this code so that errors can only have the field name with values that also exist in T. Use the keyof operator.
~~~
interface Form<T> {
values: T;
errors: any;
}</T>
const contactForm: Form<{ name: string; email: string }> = {
values: {
name: “Bob”,
email: “bob@someemail.com”
},
errors: {
emailAddress: “Invalid email address”
}
};
console.log(contactForm);
~~~
We provided an invalid error since email is not in the generic Form possible fields: name and email.
~~~
interface Form<T> {
values: T;
errors: { [K in keyof T]?: string };
}</T>
const contactForm: Form<{ name: string; email: string }> = {
values: {
name: “Bob”,
email: “bob@someemail.com”
},
errors: {
email: “Invalid email address”
}
};
console.log(contactForm);
~~~
We need to make the mapped keys optional with ? Otherwise we will have an error.
Name some mapped type modifier and their use.
The ? to make the keys in the mapped type optional. the readonly modifier to make the field readonly and the - symbol to change a readable to writable (-readonly) and -? to make a property writtable.
{ [K in keyof T]-?: TypeName } { -readonly [K in keyof T]: TypeName }
Create a RequiredProperties generic type that makes optional properties required. Use this type to map to another const called requiredBob. Use this sample:
~~~
type Contact = {
name: string;
email?: string;
};
const bob: Contact = {
name: “Bob”,
email: “bob@bob.com
};
console.log(bob);
~~~
type Contact = {
name: string;
email?: string;
};
type RequiredProperties<T> = {
[K in keyof T]-?:string;
};</T>
const bob: Contact = {
name: “Bob”,
email: “bob@bob.com”
};
const requiredBob: RequiredProperties<Contact> = {
name: "Bob",
email: "bob@bob.com"
};</Contact>
Will this generate an error. If so what is the error:
type Contact = {
name: string;
email?: string;
age?: number;
};
type RequiredProperties<T> = {
[K in keyof T]-?:string;
};</T>
const requiredBob: RequiredProperties<Contact> = {
name: "Bob",
email: "bob@bob.com"
};</Contact>
The mapped type gives all the keys a string type, but we have specified a number for age.
Create a generic type called RequiredProperties that take one type T and make the optional properties required and preserve the type of each key valye.
type RequiredProperties<T> = {
[K in keyof T]-?: T[K];
};</T>
We have changed the key type from string to T[K]. This gets the corresponding type from the type being mapped from. It is called a lookup type or sometimes an indexed access type.
What is the standard utility Required and what does it do.
Recreate it yourself.
It’s a mapped type that makes optional properties required and preserve the type of the key value:
~~~
type Required<T> = {
[P in keyof T]-?: T[P];
};
~~~</T>
What is a lookup type or an indexed access type?
In mapped types it allows to get the corresponding type being mapped from:
~~~
type RequiredProperties<T> = {
[K in keyof T]-?: T[K];
};
~~~</T>
The :T[K] is what we are referring to.
What is the typeof annotation?
The syntax for using a typeof operator in a type annotation is as follows:let newObject: typeof existingObject;
When TypeScript sees the typeof operator in a type annotation, it queries the object after it and extracts its type. So, in the example above, the type of existingObject is given to the newObject variable.
This syntax can be used on function parameters as well:function (param: typeof existingObject) { ... }
Use the typeof annotation to create a let newObject of type existingObject.
let newObject: typeof existingObject;
Can the typeof annotation be used on a function parameter? If so how?
Yes. function (param: typeof existingObject) { ... }
Use the typeof annotation to specify the type of details in the function:
~~~
const initialContactDetails = { name: “”, email: “” };
function saveContactDetails(details) {
console.log(details);
}
~~~
function saveContactDetails(details: typeof initialContactDetails) { console.log(details); }
What type is structurally the same as PersonKeys in this sample:
type Person = { firstName: string; surname: string; greet: () => void; } type PersonKeys = keyof Person;
'firstName' | 'surname' | 'greet'
or
'firstName' | 'surname'
or
` ‘greet’`
'firstName' | 'surname' | 'greet'
keyof extracts all the keys from an object including properties and methods.
Structurally, which type is equivalent to the type below?
type Votes = { [K in "apple" | "banana" | "strawberry"]: number }
This:
~~~
{
apple: number;
banana: number;
strawberry: number;
}
~~~
or
["apple", "banana", "strawberry"];
or
string[]
["apple", "banana", "strawberry"];
Create a Writable generic mapped type that removes all the readonly modifiers from this object:
type Person = { readonly name: string; readonly age: number; }
type Writable<T> = { -readonly [P in keyof T]: T[P]; }
What are conditional types ?
Conditional types are useful for removing type within a type.
Types that we create can have conditional logic, just like regular JavaScript code.T1 extends T2 ? A : B
The extends keyword is used to define the condition. If T2 is within T1 then type A is used; otherwise, type B is used.
What is NonNullable ?
It’s a standard utility type in TypeScript that is implemented using a conditional type to remove null and undefined from the type passed into it.
What will Example1 resolve to:
~~~
type Person = {
name: string;
age: number;
};
type Example1 = Person extends {} ? string : number;
~~~
string
Note: this is an example of a conditional type.
Create a utility type that remove null using conditional type. Name your type RemoveNull
type RemoveNull<T> = T extends null ? never : T;</T>