Памятуя, насколько интересными были задачки на Type | Treat 2021, решил пройти Type | Treat 2020.

Чтож, в 2020 году задачки были слабее. А главная претензия: далеко не всегда понятно из описания, что надо получить в результате. В 2021 уже стало значительно лучше. И со сложностью и с понятностью заданий.

Лента с анонсами и обсуждениями на dev.io

Day 1 Challenges

Beginner/Learner Challenge

Достаточно просто: выковырять тип из объекта

const displayHauntings = (haunting: GhostAPIResponse['hauntings'][number]) => {

Intermediate/Advanced Challenge

Подсказали, что нужно использовать conditional types. В сегодняшнем TS готовые хелперы лежат в Utility types (Exclude/Extract).

Вот решение без них

type helper<T,V> = T extends V ? T : never;
type AllCandies = helper<ResultsFromHalloween, { candy: true }>;
type AllTricks = helper<ResultsFromHalloween, { trick: true }>;
 
type inverse<T, V> = T extends V ? never : T;
type AllCandiesWithoutPeanuts = inverse<AllCandies, { peanuts: true }>;

А вот с ними:

type AllCandies = Extract<ResultsFromHalloween, { candy: true }>;
type AllTricks = Extract<ResultsFromHalloween, { trick: true }>;
 
type AllCandiesWithoutPeanuts = Exclude<AllCandies, { peanuts: true }>;

Day 2 Challenges

Beginner/Learner Challenge

Что-то перемудрил, можно же было просто typeof pumpkin:

type Pumpkin = { [keys in keyof typeof pumpkin]: typeof pumpkin[keys] }

type PumpkinFromFunction = ReturnType<typeof createExamplePumpkin>

Intermediate/Advanced Challenge

Прикольно. Код трогать нельзя, можно только внизу дописать что-то.

Вот так получилось:

type God = { god: true };
function areGods(ghosts: Array<Ghosts | God>): ghosts is God[] {
  return !!ghosts.length && ghosts.reduce((res, v) => res && !!(v as God).god, true);
}
 
type Demon = Extract<Ghosts, { demon: true }>;
function areDemons(ghosts: Ghosts[]): ghosts is Demon[] {
  return !!ghosts.length && ghosts.reduce((res, v) => res && !!(v as Demon).demon, true);
}
 
type Ectoplasmic = Parameters<typeof shockAndTrap>[0][number];
function areEctoPlasmic(ghosts: Array<Ghosts | Ectoplasmic>): ghosts is Ectoplasmic[] {
  return !!ghosts.length && ghosts.reduce((res, v) => res && !!(v as Ectoplasmic).ectoplasmic, true);
}

Day 3 Challenges

Beginner/Learner Challenge

Ужасно написанное задание. Я вообще не то сделал, что они ожидали. И посмотрел на решения других людей: похоже, что ВСЕ сделали не то.

Intermediate/Advanced Challenge

А тут как-то всё слишком просто

type TrunkOrTreatResults = {
  [key in typeof trunkOrTreatSpots[number]]: {
    done: boolean,
    who: string,
    loot: Record<string, any>
  }
}

Day 4 Challenges

Beginner/Learner Challenge

Просто добавить константности…

type Room = Readonly<{
  name: string
  doors: number
  windows: number
  ghost?: any
}>

Intermediate/Advanced Challenge

Не знаю, как это сделать на 4.1, сделал на текущем TS

const winners = {} as { [keys in Lowercase<`${typeof breeds[number]}-${typeof costumes[number]}`> ]: ReturnType<typeof decideWinner> }
... 
const id = `${breed}-${costume}`.toLowerCase() as Lowercase<`${typeof breed}-${typeof costume}`>

Посмотрел авторское решение: оказывается в 4.1 этот интринсик назывался “lowercase”. С маленькой буквы и без стрелочек

Day 5 Challenges

Beginner/Learner Challenge

Тут добавил дженерик с указанием изменяющихся параметров:

type House<N, T> = {
  doorNumber: N;
  trickOrTreat(): T;
  restock(items: T): void;
}

Применять его так:

type FirstHouse = House<1, "book" | "candy">;
type SecondHouse = House<2, "toothbrush" | "mints">;
...

Intermediate/Advanced Challenge

Тоже шаблонные литералы, и тоже интринсик. Сделал на современном:

type CapitalName = `${Capitalize<keyof typeof moviesToShow>}`;
function makeScheduler(movies: typeof moviesToShow) {
  const schedule = {} as Record<`${'getVHSFor' | 'makePopcornFor' | 'play'}${CapitalName}`, () => void>
  for (const movie in Object.keys(movies)) {
    const capitalName = movie.charAt(0).toUpperCase() + movie.slice(1) as CapitalName ;