TypeScript — удалить все свойства с определенным типом

#typescript #typescript-generics #mapped-types

#typescript #typescript-дженерики #сопоставленные типы

Вопрос:

У меня есть type который используется для проецирования ключей, присутствующих в T , на общие ключи и их типы, присутствующие в U :

 type IntersectByCommonKey<T, U, V = any> = {
  [K in keyof U]: K extends keyof T ?
  T[K] extends V ?
  U[K]
  : T[K] extends object ?
  IntersectByCommonKey<T[K], U[K], V>
  : T[K] extends object | undefined ?
  IntersectByCommonKey<T[K], U[K], V> | undefined
  : never
  : never
};
  

Ссылка IntersectByCommonKey Playground

где для каждого ключа K в U , если T имеет тот же ключ K , и если тип в K extends V , включает форму для этого ключа, в противном случае never .

С типом, возвращаемым IntersectByCommonKey я хочу удалить каждый ключ, который имеет never тип, что означает, что он также должен справляться с вложенными Record формами.

Удалить ключи верхнего уровня достаточно просто:

 type ExcludeKeysWithTypeOf<T, V> = {
  [K in keyof T]: Exclude<T[K], undefined> extends V ? never : K 
}[keyof T]

type Without<T, V> = Pick<T, ExcludeKeysWithTypeOf<T, V>>;
  

Ссылка ExcludeKeysWithTypeOf Playground

Тем не менее, я изо всех сил пытаюсь достичь того же результата для вложенных Record файлов, есть ли способ рекурсивно удалять ключи с типами never ?

Ответ №1:

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

 type Without<T, V, WithNevers = {
  [K in keyof T]: Exclude<T[K], undefined> extends V ? never 
  : (T[K] extends Record<string, unknown> ? Without<T[K], V> : T[K])
}> = Pick<WithNevers, {
  [K in keyof WithNevers]: WithNevers[K] extends never ? never : K
}[keyof WithNevers]>
  

Основная идея заключается в том, что когда у нашего T[K] будет тип записи, он снова рекурсивно вызовет себя ( Without функция уровня типа). Весь тип будет приведен к желаемой форме, в которой каждый вложенный объект будет иметь удаленные ключи, которые мы хотели.

Весь фрагмент кода — игровая площадка

Примечание: Я переместил вычисление WithNevers внутри списка аргументов типа, чтобы добиться лучшей читаемости. Мы также можем определить его как отдельный тип.