Проблема

Из входного параметра T выберите те свойства, которые присваиваемые к U. Например:

type OnlyBoolean = PickByType<
  {
    name: string;
    count: number;
    isReadonly: boolean;
    isEnable: boolean;
  },
  boolean
>; // { isReadonly: boolean; isEnable: boolean; }

Решение

В этой проблеме нам нужно перебрать все ключи объекта. В момент перебора, нам необходимо отфильтровать и оставить только те ключи, которые присваиваемые к U. Очевидно, что начать нам нужно с сопоставляющих типов.

Так что давайте начнём с того, что сделаем просто копию входящего объекта:

type PickByType<T, U> = { [P in keyof T]: T[P] };

Сначала, мы получили все ключи из T и применили к ним итерацию. На каждой итерации, TypeScript возьмет ключ и присвоит его к параметру P. Имея этот ключ, мы можем получить нужный нам тип с использованием конструкции T[P].

Теперь, применяя фильтр к этой итерации, мы сможем оставить только нужные нам ключи. Когда я говорю “фильтр”, я подразумеваю переопределение ключа в этой ситуации. Мы можем переопределить ключ на каждой итерации и оставить только интересующие нас:

type PickByType<T, U> = {
  [P in keyof T as T[P] extends U ? never : never]: T[P];
};

Обратите внимание на ключевое слово as. Это оператор, который заменит наш тип P на тип указанный с помощью as. И нас ничего не останавливает от того, чтобы написать условный тип после as. С помощью этого условного типа мы и проверим наше условие.

В случае, если тип значения из объекта присваиваемый к типу, который указан в U - оставляем ключ без изменений. Но, если тип оказывается не присваиваемый, то в таком случае мы должны его игнорировать - возвращаем never:

type PickByType<T, U> = { [P in keyof T as T[P] extends U ? P : never]: T[P] };

Таким образом, мы реализовали тип, который может фильтровать ключи объектов по их типам.

Что почитать