The Basics Flashcards

1
Q

What are mapped types?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

What is the syntax to use the keyof operator? What does it do?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What are people referring to when they talk about the the index query operator?

A

The keyof operator.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Use the keyof operator to extract the keys of this type to a let variable called keys:
type ContactDetails = { name: string; email: string };

A

let keys : keyof ContactDetails;

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

What is the type of keys here:
~~~
type ContactDetails = { name: string; email: string };
let keys : keyof ContactDetails;
~~~

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

What is a mapped type?

A

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 };

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

What is this code for and explain what it does:
type NewTypeName = { [K in UnionType]: ExistingType };

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What will be the output of the type being created here:
type ContactDetails = { [K in "name" | "email"]: string };

A
{
  name: string;
  email: string;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

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);
~~~

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Name some mapped type modifier and their use.

A

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
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

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);
~~~

A

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>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

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>

A

The mapped type gives all the keys a string type, but we have specified a number for age.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

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.

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

What is the standard utility Required and what does it do.
Recreate it yourself.

A

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>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

What is a lookup type or an indexed access type?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

What is the typeof annotation?

A

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) { ... }

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Use the typeof annotation to create a let newObject of type existingObject.

A

let newObject: typeof existingObject;

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Can the typeof annotation be used on a function parameter? If so how?

A

Yes.
function (param: typeof existingObject) { ... }

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Use the typeof annotation to specify the type of details in the function:
~~~
const initialContactDetails = { name: “”, email: “” };

function saveContactDetails(details) {
console.log(details);
}
~~~

A
function saveContactDetails(details: typeof initialContactDetails) {
  console.log(details);
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

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’`

A

'firstName' | 'surname' | 'greet'

keyof extracts all the keys from an object including properties and methods.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

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[]

A

["apple", "banana", "strawberry"];

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Create a Writable generic mapped type that removes all the readonly modifiers from this object:

type Person = {
  readonly name: string;
  readonly age: number;
}
A
type Writable<T> = {
    -readonly [P in keyof T]: T[P];
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

What are conditional types ?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

What is NonNullable ?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

What will Example1 resolve to:
~~~
type Person = {
name: string;
age: number;
};
type Example1 = Person extends {} ? string : number;
~~~

A

string
Note: this is an example of a conditional type.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

Create a utility type that remove null using conditional type. Name your type RemoveNull

A

type RemoveNull<T> = T extends null ? never : T;</T>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q

What is the use of the infer keyword?

A

There is an infer keyword that can be used within a condition in a conditional type to put the inferred type into a variable. That inferred variable can then be used within the conditional branches.

28
Q

What will be the type of item1 and item2 here:
~~~
type ArrayElementType<T> = T extends (infer E)[] ? E : T;</T>

type item1 = ArrayElementType<number[]>;

type item2 = ArrayElementType<{ name: string }>;
~~~

A

item1 is number
item2 is {name: string}

29
Q

Review this conditional type…
~~~
function addPerson(personName: string) {
return {
type: “AddPerson”,
payload: personName
};
}

function removePerson(id: number) {
return {
type: “RemovePerson”,
payload: id
};
}

type AddPersonType = typeof addPerson;
type RemovePersonType = typeof removePerson;

type FunctionReturnType<T> = T extends (...args: any) => infer R ? R : T;</T>

type Actions =
| FunctionReturnType<AddPersonType>
| FunctionReturnType<RemovePersonType>;</RemovePersonType></AddPersonType>

type Actions =
| FunctionReturnType<AddPersonType>
| FunctionReturnType<RemovePersonType>;</RemovePersonType></AddPersonType>

~~~

What is the type Actions equivalent to?

A

Actions is equivalent to the type below:

{
  type: string;
  payload: string;
} | {
  type: string;
  payload: number;
}

Note: this will work even if you are using an object instead of a function.

Here is a version that works only on functions:

function addPerson(personName: string) {
  return {
    type: "AddPerson",
    payload: personName
  };
}

function removePerson(id: number) {
  return {
    type: "RemovePerson",
    payload: id
  };
}

type AddPersonType = typeof addPerson;
type RemovePersonType = typeof removePerson;

type FunctionReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : T;

type Actions =
  | FunctionReturnType<AddPersonType>
  | FunctionReturnType<RemovePersonType>;
30
Q

What is this doing?~~~type ReturnType<T extends (…args: any) => any> = T extends ( …args: any) => infer R ? R
: any;
~~~

A

Gives the return type of the function.

31
Q

What is ReturnType

A

It is a standard TypeScript utility type. It return the return type of a function.

32
Q

What will be the type of ContactKeys in:
~~~
const person = {
name: “Fred”,
age: 30,
email: “fred@somewhere.com”
};

console.log(person);

type RemoveFromUnion<T, K> = T extends K ? never :T;

type ContactKeys = RemoveFromUnion<keyof typeof person, “age”>;
~~~

A

‘name’ | ‘email’

Note our RemoveFromUnion is the same as the standard utility type called Exclude.

33
Q

What will be the type of Contact here:
~~~
const person = {
name: “Fred”,
age: 30,
email: “fred@somewhere.com”
};

type ObjectWithKeys<T, K extends keyof T> = {
[P in K]: T[P];
};

type Contact = ObjectWithKeys<typeof person, “name” | “email”>;
~~~

A

{ name: string; email: string; }

Note: This is the same type as a standard utility type called Pick.

34
Q

What will be the type of Profile here:
~~~
const person = {
name: “Fred”,
age: 30,
email: “fred@somewhere.com”
};

type ObjectWithKeys<T, K extends keyof T> = {
[P in K]: T[P];
};

type Contact = ObjectWithKeys<typeof person, “name” | “email”>;

type RemoveFromUnion<T, K> = T extends K ? never : T;

type ObjectWithoutKeys<T, K extends keyof T> = ObjectWithKeys<
T,
RemoveFromUnion<keyof T, K>
>;

type Profile = ObjectWithoutKeys<typeof person, “name” | “email”>;
~~~

A

{ age: number; }

This is the same type as a standard utility type called Omit.

35
Q

What is the readonly modifier and what is its syntax?

A

The readonly modifier allows immutable primitive properties to be created. However, this is only compile-time immutability and not runtime immutability.

In the next lesson, we will learn how to create immutable objects and array properties.

type TypeName = {
  readonly propertyName: PropertyType;
};
interface InterfaceName {
  readonly propertyName: PropertyType;
}
class ClassName {
  constructor(public readonly propertyName: PropertyType) {}
}
36
Q

Will Typescript prevent readonly properties to be changed at runtime?

A

No. TypeScript will only raise a type error. It won’t stop readonly properties from actually being updated at runtime. TypeScript doesn’t exist at runtime to prevent such updates.

37
Q

Create a class called Vehicule with 2 properties. name (string) and topspeed a readonly number.

A
class vehicule {
    
    constructor(name:string, readonly topSpeed:number){}
}
38
Q

Is it possible to stop the reference from changing? For example on a property that is a referenced type?

A

Yes. The readonly modifier can be placed before a property that is a reference type (i.e. an object or an array) as follows:
~~~
type TypeName = {
readonly propertyName1: ObjectType;
readonly propertyName2: ArrayType;
};
~~~
This can also be used in interfaces and classes:
~~~
interface InterfaceName {
readonly propertyName1: ObjectType;
readonly propertyName2: ArrayType;
}
class ClassName {
constructor(
public readonly propertyName1: ObjectType,
public readonly propertyName2: ArrayType
) {}
}
~~~
This will stop the property’s reference from changing so that it can’t be changed to another object or array. It, however, doesn’t stop values within the property from changing.

39
Q

Will this prevent the array content from changing ?

readonly propertyName: ArrayType;

A

No. This will stop the property’s reference from changing so that it can’t be changed to another object or array. It, however, doesn’t stop values within the property from changing.
The readonly modifier can be placed before a property that is a reference type (i.e. an object or an array) as follows:

type TypeName = {
  readonly propertyName1: ObjectType;
  readonly propertyName2: ArrayType;
};
This can also be used in interfaces and classes:

interface InterfaceName {
  readonly propertyName1: ObjectType;
  readonly propertyName2: ArrayType;
}
class ClassName {
  constructor(
    public readonly propertyName1: ObjectType,
    public readonly propertyName2: ArrayType
  ) {}
}
40
Q

Explain why no error are raised here:
~~~
interface Result {
name: string;
readonly scores: number[];
readonly profile: {
level: number;
};
}
let billScores: Result = {
name: “Bill”,
scores: [90, 65, 80],
profile: {
level: 1,
},
};

billScores.scores.push(70);
billScores.profile.level = 2;
~~~

A

Putting the readonly keyword before an array property name only ensures its reference won’t change. We can mutate the array, but we can’t set it to a different array. We can put an additional readonly modifier before the array type to ensure the array items aren’t changed:

readonly scores: readonly number[];

Putting the readonly keyword before an object property name only ensures its reference won’t change. We can mutate the profile object, but we can’t set it to a different object. We need to put a readonly modifier before the level property to ensure it isn’t changed:

readonly profile: {
  readonly level: number;
};
41
Q

What is the Readonly utility type?

A

It is a handy utility type that automatically adds the readonly modifier to all the properties in an object type:

type ReadonlyType = Readonly<ExistingType>;</ExistingType>

Note: Readonly only performs a shallow readonly mapping. Readonly also doesn’t add readonly modifiers before array types.

42
Q

What is the syntax to make sure that an object reference AND it’s content can’t be mutated?

A

We need to put an additional readonly modifier before array type to prevent the array items from changing for array types.

For object types, we need to put an additional readonly modifier before properties within the object to prevent them from changing.

readonly propertyName: readonly ArrayType;

type TypeName = {
  readonly propertyName: {
    readonly subPropertyName: PropertyType;
  };
};
43
Q

Is there a way to create immutable objects at both runtime AND compile time?

A

Yes. We need to use Object.freeze

44
Q

Create a let variable assigned to an array of 3 numbers: 90, 65, 80 that will be immutable at both runtime and compile time.

A

let billScores = Object.freeze([90, 65, 80]);

45
Q

What will be the inferred type of:
let billScores = Object.freeze([90, 65, 80]);

A

readonly number[]

46
Q

Will this raise an error at runtime? What will be the error?
~~~
let billScores = Object.freeze([90, 65, 80]);
billScores.push(100);
~~~

A

Yes.
[ERR]: “Executed JavaScript Failed:”
[ERR]: Cannot add property 3, object is not extensible

47
Q

What will be the inferred type here? Will this raise an error at runtime?
let bill = Object.freeze({
name: “Bill”,
age: 30,
});

bill.age = 31;

A

Yes an error will be raised.
The inferred type will be:

Readonly<{
name: string;
age: number;
}>

48
Q

Will this raise a type a runtime error? If not why? If it does why? Will the values be changed?
~~~
let bill = Object.freeze({
name: “Bill”,
age: 30,
scores: [90, 65, 80],
profile: {
level: 1
}
});

bill.scores.push(100);
bill.profile.level = 2;
~~~

A

No type or runtime error. For compile time the Readonly type only creates a type that does a shallow readonly check. At runtime Object.freeze performs only a shallow freeze on an object.

The values are changed.

49
Q

What will be Bill score and why?
~~~
function doubleScores(scores: number[]) {
scores.forEach((score, i) => (scores[i] = score * 2));
return scores;
}

const billScores = [90, 65, 80];

console.log(billScores);
~~~

A

[180, 130, 160]
The function mutate the parameter which is a reference type. This can cause all kind of weird bugs.

50
Q

Make scores readonly here:
~~~
function doubleScores(scores: number[]) {
scores.forEach((score, i) => (scores[i] = score * 2));
return scores;
}
~~~

A
function doubleScores(scores: readonly number[]) {
  scores.forEach((score, i) => (scores[i] = score * 2));
  return scores;
}
51
Q

This will raise an error. What will be the error?
~~~
function doubleScores(scores: readonly number[]) {
scores.forEach((score, i) => (scores[i] = score * 2));
return scores;
}
~~~

A

Mutating the array here is not allowed:
scores[i] = score * 2

52
Q

Correct this function so that it does not mutate the parameter anymore:
~~~
function doubleScores(scores: readonly number[]) {
scores.forEach((score, i) => (scores[i] = score * 2));
return scores;
}
~~~

A
function doubleScores(scores: readonly number[]) {
  return scores.map((score, i) => score * 2);
}
53
Q

Wil the value of score change here?
~~~
type PersonScore = {
name: string;
score: number;
};
function doubleScore(person: PersonScore) {
person.score = person.score * 2;
return person;
}

const bill: PersonScore = {
name: “Bill”,
score: 90,
};
const doubleBill = doubleScore(bill);
console.log(bill, doubleBill);
~~~

A

The function mutates the score property within the parameter. The parameter is a reference type, so updates to it will be visible outside the scope of the function as well as inside it.

54
Q

Make person readonly in the function using the Readonly utility class:
~~~
type PersonScore = {
name: string;
score: number;
};
function doubleScore(person: PersonScore) {
person.score = person.score * 2;
return person;
}

const bill: PersonScore = {
name: “Bill”,
score: 90,
};
const doubleBill = doubleScore(bill);
console.log(bill, doubleBill);
~~~

A
function doubleScore(person: Readonly<PersonScore>) {
  return { ...person, score: person.score * 2 };
}
55
Q

What is a const assertions and what is it used for?

A

A const assertion is a kind of type assertion where the keyword const is used in place of the type:

let variableName = someValue as const;

The const assertion results in TypeScript giving the variable an immutable type based on the value structure. Here are some key points on how TypeScript infers the type from a const assertion:

For objects, all its properties will have the readonly modifier. The readonly modifier is recursively applied to all nested properties.

The readonly modifier is applied to arrays. The type will also be a fixed tuple of specific literal values in the array.

Primitives will be a literal type of the specific value of the primitive.

56
Q

Make bill deeply immutable using a const ```
type asssertion.
const bill = {
name: “Bill”,
profile: {
level: 1,
},
scores: [90, 65, 80],
};
~~~

A

const bill= {
name: “Bill”,
profile: {
level: 1,
},
scores: [90, 65, 80],
} as const ;

57
Q

What will be the type given to bill here:
~~~
const bill= {
name: “Bill”,
profile: {
level: 1,
},
scores: [90, 65, 80],
} as const ;
~~~

A
{
  readonly name: "Bill";
  readonly profile: {
    readonly level: 1;
  };
  readonly scores: readonly [90, 65, 80];
}
58
Q

Will a const type assertion raise error at runtime?

A

The const assertion gives us deep immutability at compile time only.

59
Q

Create a generic function to create a deep immutable type at runtime?

A

We can create a deepFreeze function like this one:
~~~
function deepFreeze<T>(obj: T) {
var propNames = Object.getOwnPropertyNames(obj);
for (let name of propNames) {
let value = (obj as any)[name];
if (value && typeof value === "object") {
deepFreeze(value);
}
}
return Object.freeze(obj);
}
~~~</T>

and use it like this. We add the as const type assertion to also make it compile time immutable.

const bill = deepFreeze({
  name: "Bill",
  profile: {
    level: 1,
  },
  scores: [90, 65, 80],
} as const);
60
Q

Create a generic immutable type called Immutable.

A

type Immutable<T> = {
readonly [K in keyof T]: Immutable<T[K]>;
};</T>

61
Q

Does a type error occur on the assignment on the last line?

type Readings = {
  readonly date: Date;
  readonly values: number[];
}
const readings: Readings = {
  date: new Date(),
  values: [4, 3, 5]
}
readings.values.push(1);
A

No. The items in the array are mutable. A readonly modifier needs to be placed before the type for the array items to be immutable.

62
Q

Do any type errors occur on the surname and rating assignments on the last 2 lines?

type Person = {
  firstName: string;
  surname: string;
  profile: {
    rating: string;
  }
}
const bob: Readonly<Person> = {
  firstName: "Bob",
  surname: "Keel",
  profile: {
    rating: "medium"
  }
};

  bob.surname = "Steel";
  bob.profile.rating = "high";
A

Yes on surname assignment but not on the rating assignment.

The Readonly type creates a shallow immutable type, so a type error will only occur on the surname assignment.

63
Q

What will be output to the console when the transpiled JavaScript is executed?

type Person = {
  firstName: string;
  surname: string;
}
const bob: Readonly<Person> = {
  firstName: "Bob",
  surname: "Keel"
};
bob.surname = "Smith";
console.log(bob.surname);
A

‘Smith’

64
Q

Do any type errors occur on the assignments on the last 2 lines?

const bob = {
  name: {
    firstName: "Bob",
    surname: "Keel"
  },
  profile: {
    rating: "medium"
  }
} as const;

bob.name.surname = "Smith";
bob.profile.rating = "high";
A

Yes on both lines.

65
Q

Do any type errors occur in the function?

type Person = {
  name: string;
  profile: {
    level: number;
  }
};
function increaseLevel(person: Readonly<Person>) {
  person.profile.level++;
  return person;
}
A

No. The Readonly type creates a shallow immutable type, so changing the level property won’t raise a type error.

66
Q

What will be output to the console when the transpiled JavaScript is executed? Is a type error raised?
~~~
type Person = {
firstName: string;
surname: string;
}
const bob: Readonly<Person> = Object.freeze({
firstName: "Bob",
surname: "Keel"
});
bob.surname = "Smith";
console.log(bob.surname);
~~~</Person>

A

Yes a type error is raised at compile time. Cannot assign to ‘surname’ because it is a read-only property.(2540)