Отразить и отобразить набранную форму аргумента в машинописи? - PullRequest
0 голосов
/ 05 января 2019

контекст

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

Я понимаю, что приведенное выше описание моего GQL-клиента в значительной степени упрощено, и для устранения всех сложностей, которые необходимы для совместимости со всеми функциями GQL, необходимо вручную. Это хорошо и хорошо. Моя главная цель в этом посте - строго выяснить, есть ли способ сделать это отображение отраженного типа.

Я начал рисовать жестко запрограммированный файл target client.ts, чтобы потенциальный результат выглядел здесь: https://github.com/cdaringe/gql-ts-client-codegen/blob/master/src/target.ts

Любой вклад будет оценен! Спасибо.

1 Ответ

0 голосов
/ 06 января 2019

К сожалению, вы в основном хотите ограничить тип переменной и в то же время заставить компилятор выводить тип для этой переменной. Это, к сожалению, невозможно напрямую.

Единственный способ добиться желаемого поведения - это использовать функцию. У функций могут быть параметры универсального типа, на которые наложены ограничения, но окончательный параметр типа будет выведен из фактического литерала объекта, переданного в:

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

...