扩展(映射)元组类型

时间:2019-04-13 15:23:49

标签: typescript rxjs

在泛型函数中使用元组类型时,如何“扩展”源类型?

假设我们要创建一个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的所需功能的示例为:

比方说,我需要一个具有2个泛型类型参数的函数myFunc

  • 元组类型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 个答案:

答案 0 :(得分:3)

我认为您要的是:给定一个元组类型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

使用rest tuplesconditional type inferencemapped tuples ...有点棘手...

由于TypeScript引入了函数参数和元组类型之间的类型级别对应关系以及散布元组类型的能力,因此很容易将类型 prepreending 的结果生成到元组的开头。进入剩余参数。如果L是类似于[string, number]的元组。那么函数类型(...args: L)=>void代表一个函数,分别采用两个类型为stringnumber的参数。其余参数之前可能有东西:(first: boolean, ...args: L)=>void是一个带有三个参数的函数。并且,条件类型((first: boolean, ...args: L) => void) extends ((...args: infer L2)=>void) ? L2 : never会生成一个新的元组L2,其中booleanL内容的 ……例如{{ 1}}。

但是您不想在前面加上追加。好的,通过在列表元组[boolean, string, number]前面添加任何元素,我们得到长度正确但类型错误的元组L。啊,但是我们可以映射元组!让我们使用条件类型L2映射到K的数字键L2。也就是说,如果数字键K extends keyof L ? L[K] : T位于K中(L或{{1},如果"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>>; 的类型为mapExtended()OperatorFunction<T, Push<T, R>>。因此,您可能需要使用type assertion或单个overload

由于没有安装rxjs,因此无法验证此行为。如果您需要特定于rxjs的帮助,我相信会有更多了解它的人。无论如何,希望能有所帮助。祝你好运!

相关问题