扩展(映射)元组类型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扩展(映射)元组类型相关的知识,希望对你有一定的参考价值。

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

假设我们想要创建一个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]>,这是预期的,但我不知道如何向映射的元组类型“添加”类型。相交似乎不起作用或我做错了。


EDIT:

没有rxjs的所需功能的一个例子是:

假设我需要一个函数myFunc,它有2个泛型类型参数:

  • 元组类型T,具有可变数量的元素
  • 另一种类型R.

函数的结果应该是一个元组类型,其中包含元组类型T的所有元素,并附加了类型为R的参数。

e.f.:

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]
// ...
答案

我认为你要求的是:给定一个元组类型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引入了函数参数和元组类型之间的类型级对应关系,以及将元组类型传播到rest参数的能力,因此很容易产生将类型预先添加到元组开头的结果。如果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,其中boolean被添加到L的内容...例如[boolean, string, number]

但是你不想要预先添加,你想要追加。好吧,通过在列表元组L中添加任何元素,我们得到一个正确长度的元组L2,但类型错误。啊,但我们可以映射元组!让我们使用条件类型K映射L2的数字键K extends keyof L ? L[K] : T。也就是说,如果数字键KL"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>>的通用TR。所以你可能想要使用type assertion或单个overload

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

以上是关于扩展(映射)元组类型的主要内容,如果未能解决你的问题,请参考以下文章

Scala从入门到精通之四-映射和元组

Scala学习 —— 元组&映射

使用 swig 类型映射从 c++ 方法返回向量<pair<int,int>> & 到 python 元组列表

「大数据」(八十九)Scala之映射和元组

如何在 Haskell 中连接幻像类型中的元组?

python之最强王者——元组(tuple)