Отразить и отобразить набранную форму аргумента в машинописи?

контекст

Я пытаюсь создать инструмент 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--a Partial неоднозначно, какие поля присутствуют. Я хочу, чтобы ответ клиента имел явное членство в поле как функцию ввода.

Я понимаю, что приведенное выше описание моего 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 является функцией, которая возвращает функцию (т. е. каррированную функцию), чтобы можно было указывать первый аргумент и выводить второй,

Другие вопросы по тегам