loading...
Cover image for TypeScript Exercises Bonus🦠 - Answers Part 1

TypeScript Exercises Bonus🦠 - Answers Part 1

macsikora profile image Maciej Sikora ・4 min read

Advanced TypeScript Exercises (20 Part Series)

1) Advanced TypeScript Exercises - Question 1 2) Advanced TypeScript Exercises - Answer 1 3 ... 18 3) Advanced TypeScript Exercises - Question 2 4) Advanced TypeScript Exercises - Answer 2 5) Advanced TypeScript Exercises - Question 3 6) Advanced TypeScript Exercises - Answer 3 7) Advanced TypeScript Exercises - Question 4 8) Advanced TypeScript Exercises - Answer 4 9) Advanced TypeScript Exercises - Question 5 10) Advanced TypeScript Exercises - Answer 5 11) Advanced TypeScript Exercises - Question 6 12) Advanced TypeScript Exercises - Answer 6 13) Advanced TypeScript Exercises - Question 7 14) Advanced TypeScript Exercises - Answer 7 15) TypeScript Exercises Bonus🦠 - Type of Pandemia 16) TypeScript Exercises Bonus🦠 - Answers Part 1 17) TypeScript Exercises Bonus🦠 - Answers Part 2 18) Advanced TypeScript Exercises - Question 8 19) Advanced TypeScript Exercises - Answer 8 20) Advanced TypeScript Exercises - Question 9

For details of questions and requirements please visit the questions. This post will include only answers. If you are not aware of what this is about, then please take some time with the questions article. Also I would recommend the read about TypeScript type system as a language, which can help with understanding what we are doing here.

This post will include half of the answers as questions and the solution difficulty is significantly higher than previous questions in the series.

Answer 1

The question was: Make type level function which will check if two patients can meet. CanMeet should return or true or false depends if patients can or can't meet.

In order to achieve that we should use conditional type expression. This expression can be also nested in similar matter we use standard ternary operator.

type CanMeet<A extends Patient, B extends Patient> = 
  A extends Quarantine ? false // cannot meet with anybody
  : A extends Sick ? B extends Sick ? true : false // two sick people can meet
  : A extends Healthy ? B extends Healthy ? true : false // two healthy can meet
  : false // other combination cannot meet

Full solution in the playground

Answer 2

The question was: Make type level function which will get all sick patients from the collection of patients. GetSick should filter the collection for only sick patients.

// utility types needed
type Unshift<A, T extends Array<any>> 
= ((a: A, ...b: T) => any) extends ((...result: infer Result) => any) 
? Result 
: never;
type Shift<T extends Array<any>> 
= ((...a: T) => any) extends ((a: any, ...result: infer Result) => any) 
? Result 
: never;

type GetSick<
Patients extends Patient[]
, SickPatients extends Patient[] = []
, ReducedPatients extends Patient[] = Shift<Patients>
> = {
  [K in keyof Patients]: 
  Patients[K] extends Patient
  ? ReducedPatients['length'] extends 0 ? Patients[K] extends Sick 
  ? Unshift<Patients[K], SickPatients> : SickPatients
  : Patients[K] extends Sick ? GetSick<ReducedPatients, Unshift<Patients[K], SickPatients>> 
  : GetSick<ReducedPatients,SickPatients>
  : never
}[0]

The goal was filtering only sick patients from given collection of patients. This was achieved by utility types Shift and Unshift which allow on removing/adding elements from tuple types (tuple type is exactly our collection type at the type level).

Explanation

  • Second argument SickPatients 🤒 is kind of accumulator, remember reduce function? The reason of having it is exactly accumulating sick patients.
  • ReducedPatients argument is local type variable, we use it in order to have the next version of patients array, so the version with removed next patient.
  • K in keyof Patients - we iterate over collection of patients, look at that as something like foreach loop
  • ? ReducedPatients['length'] extends 0 ? Patients[K] extends Sick ? Unshift<Patients[K], SickPatients> : SickPatients - if after removing the element we get empty collection, it means we have the last patient there, so it is collection with one item. Then we ask final question does the last item is sick patient, if so, we accumulate patient to our sick array. Unshift<Patients[K], SickPatients> is adding this patient to this list, if not we skip the patient by returning the SickPatients
  • Patients[K] extends Sick ? GetSick<ReducedPatients, Unshift<Patients[K], SickPatients>> : GetSick<ReducedPatients,SickPatients> - if the collection of patients has more then one patient, we perform the check if the current person is sick, then we add patient to the sick patients list Unshift<Patients[K], SickPatients> and recursively call our function again with -1 patient at the patients list, and +1 patient in the sick patients list. If patient was not sick then we removed it (ReducedPatients is already reduced by this patient) but we don't attach it to the sick list.
  • [0] we get first element, as we are recursively calling GetSick until the SickPatients collection is not done, so the first element have them all

Let's follow the algorithm for the example use case (its simplified view):

// patients:
type John = {name: 'John'} & Sick
type Tom = {name: 'Tom'} & Healty
type Kate = {name: 'Kate'} & Sick

type Check = GetSick<[John,Tom,Kate]>

First iteration ➰:

  • Patients: [John,Tom, Kate]
  • SickPatients: []
  • ReducedPatients: [Tom, Kate] // Shift removes John patient
  • We check if ReducedPatients is empty array, is not
  • We check if John is sick, he is
  • We add John to the beginning of SickPatients // by Unshift
  • We call next iteration

Second iteration ➰:

  • Patients: [Tom, Kate]
  • SickPatients: [John]
  • ReducedPatients: [Kate] // Shift removes Tom patient
  • We check if ReducedPatients is empty array, is not
  • We check if Tom is sick, he is not
  • We call next iteration

Third iteration ➰:

  • Patients: [Kate]
  • SickPatients: [John]
  • ReducedPatients: [] // Shift removes Kate patient
  • We check if ReducedPatients is empty array, it is
  • We check if Kate is sick, she is
  • We append Kate to the SickPatients and end the type evaluation

The result is [Kate, John]. As you can see order is reversed as we are adding items in the beginning. But the goal is achieved, we get the sick patients 👌

The full solution is available in the playground

Additional challenge 🔥

There was additional/extended question to the second one - Can you make state of the patient as an argument? And make function which will get patients for given condition? Example usage would be Get<Patients, Healthy>. As we have now GetSick implemented, can you try to make it more flexible? Put your answer in the comment section (preferred playground link).

Try yourself with the rest of questions! 🔥

There are two questions more in The Bonus Questions. As you see the solution of the first two questions, maybe it will inspire you to make other two. Don't give up, check your skills 💪.

This series will continue. If you want to know about new exciting questions from advanced TypeScript please follow me on dev.to and twitter. Be healthy and take care!

Advanced TypeScript Exercises (20 Part Series)

1) Advanced TypeScript Exercises - Question 1 2) Advanced TypeScript Exercises - Answer 1 3 ... 18 3) Advanced TypeScript Exercises - Question 2 4) Advanced TypeScript Exercises - Answer 2 5) Advanced TypeScript Exercises - Question 3 6) Advanced TypeScript Exercises - Answer 3 7) Advanced TypeScript Exercises - Question 4 8) Advanced TypeScript Exercises - Answer 4 9) Advanced TypeScript Exercises - Question 5 10) Advanced TypeScript Exercises - Answer 5 11) Advanced TypeScript Exercises - Question 6 12) Advanced TypeScript Exercises - Answer 6 13) Advanced TypeScript Exercises - Question 7 14) Advanced TypeScript Exercises - Answer 7 15) TypeScript Exercises Bonus🦠 - Type of Pandemia 16) TypeScript Exercises Bonus🦠 - Answers Part 1 17) TypeScript Exercises Bonus🦠 - Answers Part 2 18) Advanced TypeScript Exercises - Question 8 19) Advanced TypeScript Exercises - Answer 8 20) Advanced TypeScript Exercises - Question 9

Posted on Mar 23 by:

macsikora profile

Maciej Sikora

@macsikora

I am Software Developer, currently interested in static type languages (TypeScript, Elm, Reason) mostly in the frontend land

Discussion

markdown guide
 

Well, I find out the answer2 seems not work.
How can I fix it ?
Thx in advanced.
img

 

Interesting, TS can have impact. Can you share yours?