如何声明泛型类generic?

时间:2019-01-10 16:25:12

标签: typescript generics containers type-inference

我试图设计一种方法来声明可以返回Promise或rxjs Observable或具有特定返回类型的大多数Stream的函数,但是我不知道在打字稿中声明此函数的正确方法。

通常,我希望在代码中输入以下内容:

class Action<T> {
    dispatcher: Function
    constructor(dist) {
        this.dispatcher = dist
    }

    f = <T>(s: string): T<string> => this.dispatcher('hello'+s)
}
  

通过这种方式,我得到错误:'T不是通用的'

对我来说T可以是一个Promise或一个Observable或一个Stream

如果我有类似的东西,我可以声明一个特定的调度程序,该调度程序根据声明的方式返回所需的值

const promiseDispacher = (x:any)=>Promise.resolve(x)
const observableDispacher = (x:any)=>Observable.of(x)

我想拥有的功能是当我调用函数f时能够以不同的方式使用

 const a = new Action<Promise>(promiseDistpacher)
 a.f('random string').then((s: string)=>{
   ....
 })

 const a = new Action<Observable>(observableDistpacher)
 a.f('random string').subscribe((s: string)=>{
   ....
 })

已更新

f是一个执行一些操作并使用分派器将结果包装在特定的T(Promise或Stream)中并返回到执行该功能的类的函数

1 个答案:

答案 0 :(得分:2)

尽管我不确定自己在做什么,但我将尝试回答这个问题。首先,让T成为传入的调度程序返回的通用类型,例如Promise<any>Observable<any>ReadableStream<any>

class Action<T> {
  dispatcher: (x: any) => T;
  constructor(dist: (x: any) => T) {
    this.dispatcher = dist
  }
  f = (s: string) => this.dispatcher('hello' + s);
}

const promiseDispatcher = (x: any) => Promise.resolve(x)
const a = new Action(promiseDispatcher);
a.f("random string").then((s: string) => {
  // ...
}); // works

这现在有效,并且f的{​​{1}}类型被推断为Action<T>类型,并且由于(s: string) => Ta,那么Action<Promise<any>>a.f(s)。请注意,尽管Promise<any>的返回类型为f,但是T本身并不是通用函数。通用参数位于 class 上,一旦有了该类的具体实例,它的f函数也是一个具体的函数类型。


这里的一个问题是,您可能并不真的希望f成为a.f(s),而是希望看到Promise<any>。事实证明,由于TypeScript当前不非常支持higher kinded types,因此没有 general 的说法可以使Promise<string>T或{{1 }}或Promise,它们本身是通用类型。使用conditional types,您可以选择一些硬编码类型,例如ObservableReadableStreamPromise<any>,并将它们转换为Observable<any>ReadableStream<any>和{ {1}}:

Promise<string>

现在,如果我们constrain Observable<string>使用那些硬编码类型并使用上面的类型函数,我们应该会得到类似您想要的ReadableStream<string>的类型:

type AppliedToString<T> =
  T extends Promise<any> ? Promise<string> :
  T extends Observable<any> ? Observable<string> :
  T extends ReadableStream<any> ? ReadableStream<string> :
  T;

现在,如果您检查T的返回类型,您将看到它是f

这样做的不利之处是您需要维护一个硬编码列表,而且它也不是特别安全的类型,因为它可以让您传递返回// constrain T class Action<T extends Promise<any> | Observable<any> | ReadableStream<any>> { dispatcher: (x: any) => T; constructor(dist: (x: any) => T) { this.dispatcher = dist } // assert return type of f as AppliedToString<T> f = (s: string) => this.dispatcher('hello' + s) as AppliedToString<T>; } const promiseDispatcher = (x: any) => Promise.resolve(x); const a = new Action(promiseDispatcher); a.f("random string").then((s: string) => { // ... }); // works 并将其视为{ {1}} ...将在运行时爆炸。


如果我试图变得更简单,更类型安全,则将a.f限制为Promise<string>Promise<number>Promise<string>,并忘记了映射,但是需要使用传入的调度程序获取T并返回Promise<string>

Observable<string>

好的,希望其中一个或多个对您有所帮助。祝你好运!