TypeScript - функция для сопоставления типа объединения с другим типом объединения на основе дискриминатора - PullRequest
0 голосов
/ 12 мая 2018

У меня есть 2 типа различающихся объединений, которые используют одно и то же поле дискриминатора + значения. Я пытаюсь написать функцию, которая может сопоставить 1 с другой на основе дискриминатора.

, например

Тип ввода:

type InA = {
    type: 'a',
    data: string
};

type InB = {
    type: 'b',
    data: number
};

type In = InA | InB;

Тип выхода:

type OutA = {
    type: 'a',
    data: Object
};

type OutB = {
    type: 'b',
    data: Array<number>
};

type Out = OutA | OutB;

Функция отображения

// This is the function I'd like to have a better type signature
// for inferring output type based on input type
function map<In, Out>(
    in: In
): Out {
   // do something
}

Использование

// I want the compiler to infer that this is OutB based on the InB
let result = map({ type: 'b', value: 999 });

Есть ли способ написать сигнатуру функции для map , чтобы это работало?

ОБНОВЛЕНО ПОСЛЕ ОТВЕТА Мне удалось использовать модифицированную версию ответа @ titian-cernicova-dragomir. Вот общая идея + некоторый дополнительный контекст относительно того, как я его использую:

/** Http Request types **/

type RequestA = {
    type: 'names',
    url: '/names'
};

type RequestB = {
    type: 'numbers',
    url: '/numbers'
};

type Request = RequestA | RequestB;

/** Response types **/

type ResponseA = {
    type: 'names',
    data: Array<string>
};

type ResponseB = {
    type: 'numbers',
    data: Array<number>
};

type Response = ResponseA | ResponseB;

/** Helper from accepted answer */
type GetOut<T, A> = T extends { type: A } ? T : never;

/** Generic function for fetching data */
export function fetchData<
    Req extends Request,
    Res extends GetOut<Response, Req['type']>
    >(request: Req): Promise<Res> {
    return fetch(request.url)
        .then(response => response.json())
        .then(data => {
            return <Res>{
                type: request.type,
                data
            }
        });
}

// compiler knows that this is of type Promise<ResponseA> based on
// type discriminiator
let names = fetchData({
    type: 'names',
    url: '/names'
});

// compiler knows that this is of type Promise<ResponseB> based on
// type discriminiator
let numbers = fetchData({
    type: 'numbers',
    url: '/numbers'
});

1 Ответ

0 голосов
/ 12 мая 2018

Вы можете использовать условные типы для этого. Сначала мы используем условный тип для извлечения фактического строкового литерального типа, который передается в качестве аргумента (мы назовем его A). Затем, используя A, мы отфильтруем Out, чтобы получить тип от unon, который расширяет { type: A }.

type GetOut<T, A> = T extends { type : A} ? T: never; 
function map2<TIn extends In>(inParam: TIn) : TIn extends { type: infer A } ? GetOut<Out, A> : never {
    return null as any;
}

let resultA = map2({ type: 'a', data: '999' }); // result is OutA
let resultB = map2({ type: 'b', data: 999 }); // result is OutB

Детская площадка ссылка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...