#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 . Это не относится к решению, которое вы предлагаете