扩展(映射)元组类型
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 tuples,conditional type inference和mapped tuples这有点诡计...
由于TypeScript引入了函数参数和元组类型之间的类型级对应关系,以及将元组类型传播到rest参数的能力,因此很容易产生将类型预先添加到元组开头的结果。如果L
是像[string, number]
这样的元组。那么函数类型(...args: L)=>void
分别代表一个函数,它取两个string
和number
类型的参数。休息参数可以在它们之前有东西:(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>>;
请注意,mapExtended()
的实现可能不会进行类型检查,因为编译器无法轻松验证某些类型为OperatorFunction<T, Push<T, R>>
的通用T
和R
。所以你可能想要使用type assertion或单个overload。
我无法验证此行为,因为我没有安装rxjs。如果您需要特定于rxjs的帮助,我相信有更多知识的人会来。无论如何,希望有所帮助。祝好运!
以上是关于扩展(映射)元组类型的主要内容,如果未能解决你的问题,请参考以下文章