Комментарии 3
Надо заметить, что TExtractAllKeysTypeA
поддерживает более глубокую рекурсию, чем TExtractAllKeysTypeB
. У первого 48, у второго 32. Первый можно улучшить до 96, убрав рекурсивный вызов из конкатенации наружу:
Код
type TExtractAllKeysTypeA1<O, P extends string = '', K extends keyof O = keyof O> = K extends string
? O[K] extends string
? `${P}${K}`
: TExtractAllKeysTypeA1<O[K], `${P}${K}.`>
: never;
Но это не всегда хвостовая рекурсия (последнее действие тут может быть объединение дистрибутивных результатов, если их больше одного).
Хвостовая позволит увеличить глубину рекурсии до 1000:
Код
type GetNext<D, T> = D extends [infer P extends string, infer O]
? keyof O extends infer K extends keyof O
? K extends string
? O[K] extends T
? O[K] extends string
? `${P}${K}`
: [`${P}${K}.`, O[K]]
: never
: never
: never
: never;
type TailRec<D, R = never> = [D] extends [never]
? R
: TailRec<GetNext<D, object>, R | GetNext<D, string>>;
type TExtractAllKeysTypeC<O> = TailRec<['', O]>;
Здесь never
оказался в довольно свойственной для себя роли единичного элемента операции объединения - с него начинает накапливаться результат R
в типе TailRec
.
Наглядно заценить можно здесь.
Ещё один забавный факт: keyof never = string | number | symbol
. Откуда внутри пустого типа "столько всего"? Формально, never
является подтипом любого объекта, потому в нем есть все возможные поля, какие могут быть в объетах.
Поддерживаю. Любопытно. А еще мне понравились утилиты со счетчиками. Сам такие еще не использовал.
Но вот мне интересно, с чем связаны эти пределы?
Пределы нужны, чтобы не уходить в бесконечные вычисления, если в рекурсивном типе забыли дописать условие остановки. Выбраны, скорее всего, каким-то опытным путем. Вот ПР, в котором они были окончательно установлены для обычной рекурсии и увеличены для хвостовой.
Утилиты со светчиками за пределами всяких тайпчелленджей практически не встречаются)
Посмотрим на never с разных сторон?