Как представить замену ключей объекта в определении Typescript? - PullRequest
1 голос
/ 27 мая 2020

В настоящее время у меня есть следующее types:

type PossibleKeys = number | string | symbol;
type ValueOf<T extends object> = T[keyof T]; 
type ReplaceKeys<T extends Record<PossibleKeys, any>, U extends Partial<Record<keyof T, PossibleKeys>>> = 
  Omit<T, keyof U> & { [P in ValueOf<U>]: T[keyof U] };

... но, хотя он работает даже частично, он дает следующую ошибку:

Введите 'U [keyof U] 'нельзя присвоить типу' строка | номер | символ '.


Простая демонстрация :

interface Item {
  readonly description: string;
  readonly id: string;
}

interface MyInterface {
  readonly id: string;
  readonly propToReplace: number;
  readonly anotherPropToReplace: readonly Item[];
}

type ReplacedUser = ReplaceKeys<MyInterface, { propToReplace: 'total', anotherPropToReplace: 'items' }>;

В ReplacedUser Я вижу, что тип почти правильно. Предполагаемый тип:

{ id: string; total: number | readonly Item[]; items: number | readonly Item[]; }

... а я ожидаю:

{ id: string; total: number; items: readonly Item[]; }

Что я делаю не так? Сначала я хотел бы знать, как я могу express, что P должен получить значения , переданные в U, чтобы подавить ошибки Typescript, и после этого получить правильный тип для определенного c value.

1 Ответ

2 голосов
/ 27 мая 2020

Самый простой подход - инвертировать параметр типа U для вашего ReplaceKeys:

type PossibleKeys = number | string | symbol;
type ReplaceKeys<T extends {}, U extends Record<PossibleKeys, keyof T>> = Omit<T, ValueOf<U>> & {
    [K in keyof U]: T[U[K]]
};

Который затем можно использовать следующим образом:

type ReplacedUser = ReplaceKeys<MyInterface, { total: 'propToReplace', items: 'anotherPropToReplace' }>;

Если вы не может изменить форму объекта U, становится немного сложнее:

// Example types from your post
interface Item {
  readonly description: string;
  readonly id: string;
}

interface MyInterface {
  readonly id: string;
  readonly propToReplace: number;
  readonly anotherPropToReplace: readonly Item[];
}

// All possible key types
type PossibleKeys = number | string | symbol;

// Helper type to get all non-nullable values from type T
type DefinedValues<T> = NonNullable<T[keyof T]>;

// Helper type for your replacements object - a record whose values are valid keys
// and whose keys are also present in type T (the input type)
// 
// Partial is used to make sure you don't need to pass all keys of T in your replacements
type Replacements<T extends {}> = Partial<Record<keyof T, PossibleKeys>>;

// Helper type that swaps object keys for values
type Invert<T extends Replacements<C>, C extends {} = {}> = {
  [N in DefinedValues<T>]: {
    [K in keyof T]: N extends T[K] ? K : never
  }[keyof T]
}

type ReplacedKeys<T extends {}, R extends Replacements<T>> = Omit<T, keyof R | DefinedValues<R>> & {
  [N in keyof Invert<R>]: {
    [L in keyof R]: N extends R[L] ? (L extends keyof T ? T[L] : never) : never;
  }[keyof R]
}

Имейте в виду, что использование второго подхода не предупреждает вас о дублировании сопоставлений:

type ReplacedUser = ReplacedKeys<MyInterface, { propToReplace: 'total', anotherPropToReplace: 'total' }>;

Проверьте игровую площадку здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...