Отразить и отобразить набранную форму аргумента в машинописи?
контекст
Я пытаюсь создать инструмент codegen. Мне нравится использовать GraphQL, однако, когда я владею полным стеком, кажется немного глупым, что мой интерфейс вызывает мой сервер со строго определенными запросами gql. GQL строго типизирован, поэтому я должен иметь возможность предоставлять строго типизированные запросы и ответы.
проблема
Я не знаю, как создать интерфейс так, чтобы я мог рекурсивно отражать и отображать аргумент из входного типа в целевой тип. В частности, я хочу сопоставить тип моего запроса с типом ответа на запрос gql.
const query: QueryRequest<Food.IFood> = {
name: true // true ==> implies inclusion in response
}
const res = await client.food(query)
console.log(res.name) // PASS - should compile
console.log(res.nodeId) // FAIL - should not compile. `nodeId` was not present in query
// Food.IFood is a TS interface, representative of my GQL schema. GQL => TS interfaces is a solved codegen problem already
// ref: https://github.com/cdaringe/gql-ts-client-codegen/blob/master/src/__tests__/fixture/namespace.ts
QueryRequest<T>
карты моиFood.IFood
Интерфейс (не полностью) в новый тип, где ключи отображаются на bools, указывая на включение поля GQL- Тем не менее, каждый метод клиента должен был бы прослушать переданный
QueryRequest<T>
для явной формы, и каким-то образом отобразить эту явную форму, по существу, наPartial<Food.IFood>
,- Клири, я не хочу
Partial
--aPartial
неоднозначно, какие поля присутствуют. Я хочу, чтобы ответ клиента имел явное членство в поле как функцию ввода.
- Клири, я не хочу
Я понимаю, что приведенное выше описание моего GQL-клиента в значительной степени упрощено и требует ручного устранения других сложностей, необходимых для совместимости со всеми функциями GQL. Это хорошо и хорошо. Моя главная цель в этом посте - строго определить, есть ли способ сделать это отображаемое отображение типов.
Я начал набрасывать жестко заданную цель client.ts
файл для того, что я хотел бы, чтобы потенциальный вывод выглядел здесь: https://github.com/cdaringe/gql-ts-client-codegen/blob/master/src/target.ts
Любой вклад будет оценен! Благодарю.
1 ответ
К сожалению, вам нужно ограничить тип переменной, в то же время заставить компилятор определить тип этой переменной. Это, к сожалению, не возможно напрямую.
Единственный способ добиться желаемого поведения - использовать функцию. Функции могут иметь параметры универсального типа, на которые наложены ограничения, но окончательный параметр типа будет выведен из фактического литерала объекта, переданного в:
type QueryRequest<T, K extends keyof T> = {
keys: Record<K, boolean>
}
function buildQueryRequest<T>() {
return function <P extends keyof T> (o:Partial<Record<P, boolean>>) : QueryRequest<T, P>{
return null!;
}
}
interface IFood {
name: string;
nodeId: number;
}
type QueryResult<T, K extends keyof T> = Pick<T, K>
declare class Client {
food<K extends keyof IFood>(q: QueryRequest<IFood, K>) : Promise<QueryResult<IFood, K>>
}
(async function (client: Client) {
const query = buildQueryRequest<IFood>()({
name: true // true ==> implies inclusion in response
})
const res = await client.food(query)
console.log(res.name) // PASS - should compile
console.log(res.nodeId) // error
})
buildQueryRequest
является функцией, которая возвращает функцию (т. е. каррированную функцию), чтобы можно было указывать первый аргумент и выводить второй,