Pull to refresh

Comments 8

Я вот так решил:

type ExtractFromObject<O, K> = [never[], K] extends [O, number | `${number}`]
  ? O[number & keyof O] | undefined
  : K extends keyof O ? O[K] : undefined;

type GetWithArray<O, K> = O extends null | undefined
  ? O
  : K extends readonly []
    ? O
    : K extends readonly [infer Key, ...infer Rest]
      ? GetWithArray<ExtractFromObject<O, Key>, Rest>
      : never;

У вас не совсем правильно работает на массивах, например, у массивов есть свойство 'length', которое должно быть number.

Выкидывать null и undefined можно с помощью типа NonNullable < T >, который под капотом просто T & {}. Почему так? Потому что {} - это "всё кроме null и undefined"

@AlexandroppolusОгонь решение ? Я проверил, что оно работает с TypeScript 4.4, когда я изначально писал статью, так что большой респект.

У вас не совсем правильно работает на массивах, например, у массивов есть свойство 'length'

Не очень понял, как это можно использовать.

Если предлагается использовать { length: number } для отсеивания массивов от кортежей, есть пограничные случаи структур данных, в которых тоже есть свойство length, но они при этом не являются массивом/кортежем. Т.к. я работаю в видео стриминге, для меня самый простой пример - это TimeRanges - https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges. Но это редкий случай, их 1%, поэтому в целом можно. Но я не стал так делать.

Выкидывать null и undefined можно с помощью типа NonNullable < T >, который под капотом просто T & {}

С NonNullable и {} я согласен. Уточнение, что {} работает только с версии 4.8, а писал я статью в 2021 году, когда был 4.4, но все равно валидно.

Комментируя ваше решение:

Вынесение null | undefined очень хорошая идея, т.к. я рассматривал ветки очень локально, не пытаясь найти общий знаменатель во всем решении.

В остальном: не хочу быть занудой, но условные типы в вашем решении свернуты, то есть если бы я свои свернул, получилось бы ваше решение.

[never[], K] extends [O, number | `${number}`]

Это сокращенная версия, или свертка, для never[] extends O ? K extends number | `${number}` ? ... : never : never . Я старался избегать такие хаки, про это можно отдельную статью писать.

O[number & keyof O]

Это свертка для значений массива, что-то типа type NumericValues<O> = number extends keyof O ? O[number] : never;, который потом вызвался NumericValues<O> (тут number & keyof O - свертка для условного типа в NumericValues )

В целом, конечно, очень красиво и эффектно. Я допишу статью, как до этого дойти и добавлю пункт между 3. Получение пути из массива и кортежи и 4. Общее решение. Спасибо за идею ?

Не очень понял, как это можно использовать.

Ну, например, GetWithArray < ProductionObject, ['versions', 'length'] > должен быть number - берем длину массива.

Я тут слегка поправил своё решение, точнее ExtractFromObject . В предыдущем были косяки с кортежами и массивами:

1) для кортежа ключ number возвращал все свои значения без undefined, а должен с ним.

2) неправильно работало с ключами-объединениями для массивов (см. примеры в extendedCases), в том числе never. Ключи never срезают всё кроме null и undefined, т.к. эти двое не индексируются.

3) для массива ключ never возвращал все его значения (думаю, это баг TS), а должен вернуть never, как в объектах

Ага, понял. Спасибо за развернутый ответ

В дополнение, по ощущению, ваше решение работает быстрее, чем мое, т.к. в моем решении приходится обрабатывать очень много условных типов. Потестировал сегодня ночью свое решение в ts-essentials (README для PathValue) и нарвался на T2589: Type instantiation is excessively deep and possibly infinite, когда добавляю StringPath extends Paths<Type> для параметра path: StringPath

Можете кинуть ссылку на Playground с ошибкой?

Сорри, не сразу увидел вопрос, вот пример: https://tsplay.dev/WJ09kN

Правда я чекнул, что если взять ваше решение, то будет та же самая ошибка - https://tsplay.dev/wjL5Mm

Я уверен, что проблема где-то на поверхности, но не копал еще вглубь

Глянул код, не смог найти причину ошибки. Взял вариан с моим решением, и написал так, поменяв Paths, вроде попустило. Навскидку примерно всё то же самое, почему работает, непонятно. Есть дублирование кода в Paths, но не стал заморачиваться.

Sign up to leave a comment.

Articles