Расширение (сопоставление) типов кортежей - PullRequest
1 голос
/ 13 апреля 2019

Когда вы используете типы кортежей в универсальных функциях, как вы можете «расширить» тип источника?

Допустим, мы хотим создать оператор отображения rxjs, который возвращает наблюдаемое значение (я) источника вместе с другим отображеннымзначение:

export function mapExtended<T, R>(mapping: (input: [T]) => R): OperatorFunction<[T], [T, R]>;
export function mapExtended<T1, T2, R>(mapping: (input: [T1, T2]) => R): OperatorFunction<[T1, T2], [T1, T2, R]>;
export function mapExtended<T1, T2, T3, R>(mapping: (input: [T1, T2, T3]) => R): OperatorFunction<[T1, T2, T3], [T1, T2, T3, R]>;
export function mapExtended<R>(mapping: (input: any) => R): OperatorFunction<any, {}> {
    return (source$: Observable<any>) => source$.pipe(
        map(input => {
            const mappingResult = mapping(input);
            return [...input, mappingResult];
        }),
    );
}

Кажется, что работает, но перегрузки не обнаружены должным образом.Тип возвращаемого значения для test: Observable<[[number, number], number]> вместо ожидаемого Observable<[number, number, number]>:

const test = combineLatest(of(1), of(2)).pipe(
    mapExtend(([s1, s2]) => s1 + s2),
);

Существует ли какая-либо проверка типа, чтобы сказать, что T не может быть типом кортежа?


Я пытался выполнить то же самое с поддержкой отображаемых типов для кортежей, но безрезультатно, поскольку я не могу (или не знаю, как) «расширить» отображаемый тип:

type MapExtended2Input<T> = { [P in keyof T]: T[P] };
function mapExtended2<T extends any[], R>(mapping: (input: MapExtended2Input<T>) => R): OperatorFunction<MapExtended2Input<T>, [MapExtended2Input<T>, R]> {
    return (source$: Observable<MapExtended2Input<T>>) => source$.pipe(
        map(input => {
            const mappingResult = mapping(input);
            const result: [MapExtended2Input<T>, R] = [input, mappingResult];
            return result;
        }),
    );
}

const test2 = combineLatest(of(1), of(2)).pipe(
    mapExtended2(([s1, s2]) => s1 + s2),
);

Здесь тип возвращаемого значения также Observable<[[number, number], number]>, что ожидается, но я не знаю, как «добавить» тип в сопоставленный тип кортежа.Пересечение не работает, или я делаю это неправильно.


РЕДАКТИРОВАТЬ:

Примером желаемой функциональности без rxjs будет:

Давайтескажем, мне нужна функция myFunc, которая имеет 2 параметра общего типа:

  • тип кортежа T с переменным числом элементов
  • другой тип R

Результатом функции должен быть тип кортежа, содержащий все элементы типа кортежа T с добавленным к нему параметром типа R.

Например:

myFunc<T, R>(x, y); // Should return [T, R]
myFunc<[T1, T2], R>(x, y); // Should return [T1, T2, R]
myFunc<[T1, T2, T3], R>; // Should return [T1, T2, T3, R]
// ...

1 Ответ

3 голосов
/ 13 апреля 2019

Я думаю, что вы запрашиваете: с учетом типа кортежа L (для «списка») и другого типа T создайте новый тип кортежа с добавлением T в конце L.Сортировка полученного типа, если вы должны были позвонить l.push(t)Вы можете сделать это следующим образом:

type Push<L extends any[], T> =
  ((r: any, ...x: L) => void) extends ((...x: infer L2) => void) ?
  { [K in keyof L2]-?: K extends keyof L ? L[K] : T } : never

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

Достаточно просто получить результат с добавлением типа к началу кортежа, поскольку TypeScript ввел соответствие на уровне типов между параметрами функции и типами кортежей, ивозможность распределять типы кортежей в параметры покоя.Если L является кортежем типа [string, number].тогда тип функции (...args: L)=>void представляет функцию, принимающую два параметра типов string и number соответственно.Параметр rest может иметь перед собой элементы: (first: boolean, ...args: L)=>void - это функция, принимающая три параметра.И условный тип ((first: boolean, ...args: L) => void) extends ((...args: infer L2)=>void) ? L2 : never создает новый кортеж L2, где boolean равен с добавлением к содержимому L ..., например [boolean, string, number].

Новы не хотите добавлять, вы хотите добавляете .Итак, добавив любой элемент к вашему списку кортежей L, мы получим кортеж L2 правильной длины, но с неправильными типами.Ах, но мы можем сопоставить кортежи!Давайте сопоставим цифровые клавиши K из L2, используя условный тип K extends keyof L ? L[K] : T.То есть, если цифровая клавиша K находится в L ("0" или "1", если L - длина 2), просто используйте соответствующий тип из L.Если нет, то это должен быть тот самый конец ("2", если L - длина 2), поэтому используйте новый тип T.Это должно привести к добавлению T к концу L.

Давайте удостоверимся, что это работает:

type P = Push<[1, 2, 3], 4>; // type P = [1, 2, 3, 4]

Выглядит хорошо.Теперь вы можете объявить свой mapExtended() следующим образом:

declare function mapExtended<T extends any[], R>(
  mapping: (input: T) => R
): OperatorFunction<T, Push<T, R>>;

Обратите внимание, что реализация 1064 * из mapExtended() может не проверять тип, потому что компилятор не может легко проверить, что что-тотипа OperatorFunction<T, Push<T, R>> для универсальных T и R.Поэтому вы, вероятно, захотите использовать утверждение типа или одну перегрузку .

Я не могу проверить поведение этого, потому что у меня не установлен rxjs,Если вам нужна помощь по rxjs, я уверен, что придет кто-то более знающий об этом.Во всяком случае, надеюсь, что это помогает.Удачи!

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