TypeScript泛型:泛型函数类型和对象文字类型的调用签名不相等(类型'T'不能分配给类型'T') [英] TypeScript generics: Inequivalence of generic function type and call signature of an object literal type (Type 'T' is not assignable to type 'T')

查看:126
本文介绍了TypeScript泛型:泛型函数类型和对象文字类型的调用签名不相等(类型'T'不能分配给类型'T')的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在写一个RxJS运算符,它等待输入运算符完成,然后对第二个运算符进行 switchMap (与 switchMap一样动态生成)

I'm writing an RxJS operator which waits for the input operator to complete and then does a switchMap to a second operator (which is generated on the fly just as with switchMap). I have come up with two versions of my code, one which works and one which doesn't and I'm struggling to wrap my head around why that is.

有效版本:

import { Observable } from "rxjs"; // OperatorFunction,
import { defaultIfEmpty, last, switchMap } from "rxjs/operators";

// This definition of OperatorFunction is more or less equivalent to the
// definition in rxjs/src/internal/types.ts
interface OperatorFunction<T, S> {
    (input: Observable<T>): Observable<S>;
}

interface ObservableGenerator<T, S> {
    (value: T): Observable<S>;
}

export function switchMapComplete<T, S>(project: ObservableGenerator<T, S>): OperatorFunction<T, S> {
    function mapper(obs1: Observable<T>): Observable<S> {
        return obs1.pipe(
            defaultIfEmpty(null),
            last(),
            switchMap(project)
        );
    }

    return mapper;
}

无效的版本(请注意,唯一更改的是 OperatorFunction OperatorGenerator 的定义):

The version that doesn't work (note that the only thing that changed are the definitions of OperatorFunction and OperatorGenerator):

import { Observable } from "rxjs";
import { defaultIfEmpty, last, switchMap } from "rxjs/operators";

type OperatorFunction2<T, S> = <T, S>(obs: Observable<T>) => Observable<S>;

type ObservableGenerator2<T, S> = <T, S>(value: T) => Observable<S>;

export function switchMapComplete2<T, S>(project: ObservableGenerator2<T, S>): OperatorFunction2<T, S> {
    function mapper(obs1: Observable<T>): Observable<S> {
        return obs1.pipe(
            defaultIfEmpty(null),
            last(),
            switchMap(project)
        );
    }

    return mapper;
}

后一版本导致编译器引发以下异常:

The latter version causes the compiler to throw the following exception:

error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<S>'.
  Type '{}' is not assignable to type 'S'.
util.ts(49,5): error TS2322: Type '(obs1: Observable<T>) => Observable<S>' is not assignable to type 'OperatorFunction2<T, S>'.
  Types of parameters 'obs1' and 'obs' are incompatible.
    Type 'Observable<T>' is not assignable to type 'Observable<T>'. Two different types with this name exist, but they are unrelated.
      Type 'T' is not assignable to type 'T'. Two different types with this name exist, but they are unrelated.

我对此感到非常惊讶,并且花了很长时间才提出工作版本,作为TypeScript docs 说两个版本应该等效(据我了解).

I am really surprised by this and it took me ages to come up with the working version as the TypeScript docs say that both versions should be equivalent (as far as I understand).

我很感谢有任何关于为什么我的等效性失效的指示.

I'd be grateful for any pointers as to why in my case the equivalence breaks down.

PS:对于需要类似于我的RxJS运算符的任何人,这是另一个(有效的)解决方案,它稍微简单一些,并充分利用了RxJS已经提供的类型:

PS: For anyone in need of an RxJS operator similar to mine, here's another (working) solution which is slightly simpler and makes full use of the types that RxJS already provides:

import { Observable, ObservableInput, OperatorFunction, pipe } from "rxjs";
import { defaultIfEmpty, last, switchMap } from "rxjs/operators";

export function switchMapComplete<T, S>(project: (value: T) => ObservableInput<S>): OperatorFunction<T, S> {
    return pipe(
        defaultIfEmpty(null),
        last(),
        switchMap(project)
    );
}

推荐答案

首先,应更改类型 OperatorFunction2< T,S>.=< T,S>(obs:Observable< T>)=>可观察的< S> 只是类型的OperatorFunction2 =< T,S>(obs:Observable< T>)=>可观察的< S> ,因为您没有在类型别名的定义中使用外部的 T S .内部的< T,S> 遮盖了外部名称.然后对 ObservableGenerator2 进行类似的更改.

First off, you should change type OperatorFunction2<T, S> = <T, S>(obs: Observable<T>) => Observable<S> to just type OperatorFunction2 = <T, S>(obs: Observable<T>) => Observable<S> because you are not using the outer T or S in the definition of the type alias. The <T, S> in the inside shadows the outer names. And make the analogous change to ObservableGenerator2.

请注意,类型F< T>=(x:T)=>void 不等于 type G =< T>(x:T)=> void .TypeScript不允许完全通用值.类型 F 是泛型的,并且是一个具体的函数,并且必须为 F 提供一个要使用的类型参数( F 不好,F< string> 好).类型 G 是引用泛型函数的具体类型,并且不能为 G 提供类型参数( G< string> 错误, G 好).类型为 F< string> 的值是具体的,并且只能接受 string 函数输入.类型为 G 的值是通用的,可以接受任何输入.

Note that type F<T> = (x:T) => void is not equivalent to type G = <T>(x:T)=>void. TypeScript doesn't allow fully generic values. Type F is generic and refers to a concrete function, and F must be given a type parameter to be used (F bad, F<string> good). Type G is a concrete type that refers to a generic function, and G cannot be given a type parameter (G<string> bad, G good). A value of type F<string> is concrete and can only accept string function inputs. A value of type G is generic and can accept any input.

并不是说类型是完全不相关的.他们只是不相等.我希望可以为您提供更多帮助,但是我没有在任何地方安装RxJS,因此以下代码可能仍然有错误.不过,我会在这里向您展示我会尝试的方法:

It's not that the types are completely unrelated; they're just not equivalent. I wish I could help you more but I don't have RxJS installed anywhere, so the following code may still have errors. Still, I'll show you what I'd be trying here:

// concrete types referring to generic functions
type OperatorFunction2 = <T, S>(obs: Observable<T>) => Observable<S>;
type ObservableGenerator2 = <T, S>(value: T) => Observable<S>;

// a concrete function which takes a generic function and returns a generic function
export function switchMapComplete2(project: ObservableGenerator2): OperatorFunction2 {
  // a generic function
  function mapper<T, S>(obs1: Observable<T>): Observable<S> {
    return obs1.pipe(
      defaultIfEmpty(null),
      last(),
      switchMap(project)
    );
  }

  return mapper;
}

希望您能走上正确的道路.您可能需要深入研究 mapper 的实现并修复更多签名.无论如何,祝你好运!

Hope that puts you on the right track. You may need to go down into the implementation of mapper and fix more signatures. Anyway, good luck!

这篇关于TypeScript泛型:泛型函数类型和对象文字类型的调用签名不相等(类型'T'不能分配给类型'T')的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆