Как мне определить тип на основе проекции запроса

#typescript #typescript-typings

#typescript #typescript-типизации

Вопрос:

Есть ли способ определения некоторого общего динамического типа в TypeScript, который позволил бы мне определить тип на основе параметров проекции?

Допустим, вы хотите выполнить запрос к базе данных или выполняете запрос GraphQL. У вас будет такая функция, как

 interface Person {
  id: string;
  firstName: string;
  lastName: string;
}

function getPeople(projection: (keyof Person)[]): Promise<Partial<Person>[]> {...}
  

Я имею в виду, что если я сейчас выполню getPeople(['id', 'firstName']) , то возвращаемые объекты никогда не будут иметь никакого свойства lastName , поскольку проекция была настроена так, чтобы никогда не загружать это в память. Есть ли какой-либо способ, которым я мог бы указать это на уровне типа, а не просто использовать Partial<Person> , который все еще позволяет мне ссылаться на неинициализированные свойства?

Ответ №1:

Подсказка здесь в том, что нам нужно сделать getPeople generic таким образом, чтобы мы могли ссылаться на тип параметров в возвращаемом типе. Это должно сделать то, чего вы хотите достичь:

 interface Person {
  id: string;
  firstName: string;
  lastName: string;
}

type Fields<P, keys extends keyof P> = {
  [key in keys]: P[key];
};

declare function getPeople<keys extends keyof Person>(
  projection: keys[]
): Promise<Array<Fields<Person, keys>>>;
  

Комментарии:

1. TypeScript иногда может быть таким. Я считаю, что это помогает спросить себя: «Как мне описать вывод в терминах ввода», создать тип (в нашем случае Fields ), который делает это, а затем ввести параметр типа в нашу общую функцию, которая вызывает тип, который мы ранее создали.

Ответ №2:

Все, что вам нужно сделать, это

 function getPeople<K extends keyof Person>(projection: K[]):
Promise<Pick<Person, K>[]>
  

Ответ №3:

 interface EntityType {
  a: string;
  b: string;
  c: string;
}

const EntityObject: EntityType = { a: 'a', b: 'b', c: 'c' };

function EntityProjection<T extends keyof EntityType>(projection?: Array<T>): Pick<EntityType, T> {
  return EntityObject;
}

function useIt() {
  const x = EntityProjection(['a']);
  
  x.b // will throw error
}
  

Ответ №4:

Вы можете сделать свойства необязательными. Используйте ? key, чтобы сделать его необязательным.

  interface Person {
      id: string;
      firstName?: string;
      lastName?: string;
    }

   function getPeople(projection: (keyof Person)[]): Promise<Partial<Person>[]> 
   {...}
  

Теперь, кроме id, все свойства являются необязательными.

Комментарии:

1. это здорово, но это не то, к чему я стремился. Я хочу, чтобы этот вызов — (await getPeople(['id']))[0].firstName — выдал мне ошибку компилятора, в которой говорилось: «‘FirstName’ не существует для типа X»

2. Определите возвращаемый тип следующим function getPeople(projection: (keyof Person)[]): Promise<Person[]> {...} образом Попробуйте это, я надеюсь, это сработает

3. ну … это работает в целом, но в этом случае, когда я вызываю person.lastName , все это хорошо компилируется, и я получаю значение undefined , тогда как я бы хотел, чтобы его стало невозможно скомпилировать в JS в первую очередь, задолго до того, как я его запущу, если я когда-либо ссылаюсь на свойство LastName . Это не относится к решению, которое вы предлагаете