Typescript 函数重载不兼容
Typescript Function Overload Incompatibility
我一直在实施 NgRx 存储并发现自己在使用模式
concatMap(action => of(action).pipe(
withLatestFrom(this.store.pipe(select(fromBooks.getCollectionBookIds)))
)),
(从 https://ngrx.io/guide/effects 的底部开始)
考虑到它会占用多少空间,我想写一个方便的实用程序运算符来处理它。
所以我想出了一个应该可行的实现:
export function concatMapLatestFrom<A extends Action>(
...xs: Array<ObservableInput<never>>
): OperatorFunction<A, unknown> {
return function (source: Observable<A>): Observable<unknown> {
return source.pipe(
concatMap((action) => of(action).pipe(withLatestFrom(...xs))),
);
};
}
我写了一些正确类型的重载:
export function concatMapLatestFrom<X1, A extends Action>(
source1: ObservableInput<X1>,
): { (source1: X1): OperatorFunction<A, [A, X1]> };
export function concatMapLatestFrom<X1, X2, A extends Action>(
source1: ObservableInput<X1>,
source2: ObservableInput<X2>,
): { (source1: X1, source2: X2): OperatorFunction<A, [A, X1, X2]> };
export function concatMapLatestFrom<X1, X2, X3, A extends Action>(
source1: ObservableInput<X1>,
source2: ObservableInput<X2>,
source3: ObservableInput<X3>,
): {
(source1: X1, source2: X2, source3: X3): OperatorFunction<
A,
[A, X1, X2, X3]
>;
};
但出于某种原因,它认为重载签名与实现不兼容。如果我注释掉其中一个,它会重复一行中的下一个。
它让我逃避了这到底有什么问题,如果编译器能告诉我为什么它不兼容,但不幸的是它没有给出任何细节。
当您使用重载时,类型信息来自重载,而实现只需要处理所有可能的参数。类型参数和 return 类型将根据选定的重载进行更改,因此将其保留可能会解决问题。
export function concatMapLatestFrom(
...xs: Array<ObservableInput<never>>
) {
return function(source: Observable<any>): Observable<unknown> {
return source.pipe(
concatMap(action => of(action).pipe(withLatestFrom(...xs)))
);
};
}
我认为 罪魁祸首 是您在那里使用的 never
和 unknown
类型。
所以,如果你有
of(1)
.pipe(
concatMapLatestFrom(of('john'), of(true))
).subscribe(observer)
那么,如果我理解正确的话,observer
应该收到一个 [number, string, boolean]
元组。
据我所知,concatMapLatestFrom
并没有说得很清楚:
function concatMapLatestFrom (): OperatorFunction<A, unknown> {}
所以,我们只得到了该元组的第一个元素,即源的类型。剩下要做的是 属性 根据提供给 concatMapLatestFrom
.
的 ObservableInput
类型推断其他类型
考虑到这一点,我的方法是:
export function concatMapLatestFrom<
A extends Action,
U extends Array<ObservableInput<any>>,
R = Unshift<ObservedValueTupleFromArray<U>, A>
> (
...xs: U
): OperatorFunction<A, R> {
return function (source: Observable<A>): Observable<R> {
return source.pipe(
concatMap((action) => of(action).pipe(withLatestFrom(...xs))),
);
};
}
使用的类型可以从'rxjs'
导出:
// Mapped tuples: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html#mapped-types-on-tuples-and-arrays
// E.g [Observable<number>, Observable<string>] --> [number, string]
export type ObservedValueTupleFromArray<X> =
X extends Array<ObservableInput<any>>
? { [K in keyof X]: ObservedValueOf<X[K]> }
: never;
// Simply extract the value
export type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
// Unshift<[B, C], A> --> [A, B, C]
export type Unshift<X extends any[], Y> =
((arg: Y, ...rest: X) => any) extends ((...args: infer U) => any)
? U
: never;
我一直在实施 NgRx 存储并发现自己在使用模式
concatMap(action => of(action).pipe(
withLatestFrom(this.store.pipe(select(fromBooks.getCollectionBookIds)))
)),
(从 https://ngrx.io/guide/effects 的底部开始)
考虑到它会占用多少空间,我想写一个方便的实用程序运算符来处理它。
所以我想出了一个应该可行的实现:
export function concatMapLatestFrom<A extends Action>(
...xs: Array<ObservableInput<never>>
): OperatorFunction<A, unknown> {
return function (source: Observable<A>): Observable<unknown> {
return source.pipe(
concatMap((action) => of(action).pipe(withLatestFrom(...xs))),
);
};
}
我写了一些正确类型的重载:
export function concatMapLatestFrom<X1, A extends Action>(
source1: ObservableInput<X1>,
): { (source1: X1): OperatorFunction<A, [A, X1]> };
export function concatMapLatestFrom<X1, X2, A extends Action>(
source1: ObservableInput<X1>,
source2: ObservableInput<X2>,
): { (source1: X1, source2: X2): OperatorFunction<A, [A, X1, X2]> };
export function concatMapLatestFrom<X1, X2, X3, A extends Action>(
source1: ObservableInput<X1>,
source2: ObservableInput<X2>,
source3: ObservableInput<X3>,
): {
(source1: X1, source2: X2, source3: X3): OperatorFunction<
A,
[A, X1, X2, X3]
>;
};
但出于某种原因,它认为重载签名与实现不兼容。如果我注释掉其中一个,它会重复一行中的下一个。
它让我逃避了这到底有什么问题,如果编译器能告诉我为什么它不兼容,但不幸的是它没有给出任何细节。
当您使用重载时,类型信息来自重载,而实现只需要处理所有可能的参数。类型参数和 return 类型将根据选定的重载进行更改,因此将其保留可能会解决问题。
export function concatMapLatestFrom(
...xs: Array<ObservableInput<never>>
) {
return function(source: Observable<any>): Observable<unknown> {
return source.pipe(
concatMap(action => of(action).pipe(withLatestFrom(...xs)))
);
};
}
我认为 罪魁祸首 是您在那里使用的 never
和 unknown
类型。
所以,如果你有
of(1)
.pipe(
concatMapLatestFrom(of('john'), of(true))
).subscribe(observer)
那么,如果我理解正确的话,observer
应该收到一个 [number, string, boolean]
元组。
据我所知,concatMapLatestFrom
并没有说得很清楚:
function concatMapLatestFrom (): OperatorFunction<A, unknown> {}
所以,我们只得到了该元组的第一个元素,即源的类型。剩下要做的是 属性 根据提供给 concatMapLatestFrom
.
ObservableInput
类型推断其他类型
考虑到这一点,我的方法是:
export function concatMapLatestFrom<
A extends Action,
U extends Array<ObservableInput<any>>,
R = Unshift<ObservedValueTupleFromArray<U>, A>
> (
...xs: U
): OperatorFunction<A, R> {
return function (source: Observable<A>): Observable<R> {
return source.pipe(
concatMap((action) => of(action).pipe(withLatestFrom(...xs))),
);
};
}
使用的类型可以从'rxjs'
导出:
// Mapped tuples: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html#mapped-types-on-tuples-and-arrays
// E.g [Observable<number>, Observable<string>] --> [number, string]
export type ObservedValueTupleFromArray<X> =
X extends Array<ObservableInput<any>>
? { [K in keyof X]: ObservedValueOf<X[K]> }
: never;
// Simply extract the value
export type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;
// Unshift<[B, C], A> --> [A, B, C]
export type Unshift<X extends any[], Y> =
((arg: Y, ...rest: X) => any) extends ((...args: infer U) => any)
? U
: never;