Type challenges
На Ютубе наткнулся на видосик, где человек разбирал какую-то задачку из челленджа. Заинтересовался, что это такое за челлендж, и сам того не заметил, как включился. Интересный оказался, но сложность местами не соответствует разделу, как мне показалось. В частности, некоторые Easy совсем не изи оказались. Это если их с максимальным уровнем сложности “Extreme” сравнивать.
https://github.com/type-challenges/type-challenges
Ниже решения первого раздела задач
Warm Up
Hello World
type HelloWorld = string // expected to be a string
Easy
Pick
type MyPick<T, K extends keyof T> = { [V in K]: T[V] };
Readonly
type MyReadonly<T> = { readonly [K in keyof T]: T[K] }
Tuple to Object
type TupleToObject<T extends readonly (string | number](]> = { [K in T[number]]: K }
First of Array
type First<T extends unknown[]> = T extends [] ? never : T[0]
Length of Tuple
На этой штуке завис… Вот что значит, не читать сообщения компилятора. Думал, что он ругается, что .length нет, но нет, он ругался на точку и просил [‘length’].
type Length<T extends readonly unknown[]> = T['length']
Exclude
Тут тоже залип. Напрочь забыл как раскручиваются юнионы типов.
type MyExclude<T, U> = T extends U ? never : T;
Awaited
Тут было проще, но пришлось рекурсивно вложенные Promise раскрутить. И само решение уже не то, чтобы уж совсем “easy” получилось
type MyAwaited<T extends { then: (arg: any) => any }> = T['then'] extends (a: (value: infer R) => any) => any ? (R extends Promise<any> ? MyAwaited<R> : R) : never
If
А вот это было реально простым.
type If<C extends boolean, T, F> = C extends true ? T : F
Concat
И это опять простое
type Concat<T extends any[], U extends any[]> = [...T, ...U]
Includes
Нифига себе изи! Справился только через рекурсивный разбор и строгое сравнение типов из-за совершенно нелогичных кейсов, вида Includes<[1], 1 | 2>
, который должен отдавать false, хотя, казалось бы, что проверяем список [1]
на соответствие 1
или 2
… Но нет, в тестах хотят именно строгий матч и чтобы тут был false.
И сравнение типов на равенство подсмотрел на SO: проверки на взаимный extends недостаточно из-за того что true extends boolean
и boolean extends true
- оба true
, хотя казалось бы…
type Exact<A, B> = (<T>() => T extends A ? 1 : 0) extends (<T>() => T extends B ? 1 : 0) ? true : false
type Includes<T extends Array<any>, U> = T extends [infer V, ...infer Rest] ? Exact<V, U> extends true ? true : Includes<Rest, U> : false
Push
Ну, тут тривиально:
type Push<T extends Array<unknown>, U> = [...T, U]
Unshift
И это тоже, по сути, тот же Push, только отзеркаленный
type Unshift<T extends Array<unknown>, U> = [ U, ...T]
Parameters
Написать собственный Parameters. Ну, такое я писал, пока его ещё не было в стандартной библиотеке. Но ни разу такое не изи, хотя и не сказать, чтобы очень сложно.
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never
Extreme
Get Readonly Keys
Проверки ради попробовал сразу extreme-уровень, первый же Get Readonly Keys
. Оказалось, что пол дела уже сделано. Сравнение придумалось сразу: Exact у меня уже был (из Includes), а долго провозился со способом хранения найденных ключей.
type Exact<A, B> = (<T>() => T extends A ? 1 : 0) extends (<T>() => T extends B ? 1 : 0) ? true : false
type GetReadonlyKeys<T> = {
[K in keyof T]-?: Exact<Pick<T, K>, Readonly<Pick<T, K>>> extends true ? K : never
}[keyof T];
По сравнению с Includes, радикального усложнения в Get Readonly Keys я не заметил. Получается, что или Easy не весь такой уж изи, по крайней мере, некоторые задачи из него. Или Extreme не очень-то и экстремальный. Я больше склоняюсь к первому варианту.