如何将函数类型应用到生成器?

问题描述 投票:0回答:1

我有以下生成器函数:

async function * filterIterable (iter, predicate) {
  let i = 0
  for await (const val of iter) {
    if (predicate(val, i++)) {
      yield val
    }
  }
}

我想针对以下两种情况输入:

// type predicate
const nonNullable = <T>(val: T | undefined | null): val is T =>
  val !== undefined && val !== null

async function * gen1 () {
  yield 1
  yield 2
  yield 3
}

async function * gen2 () {
  yield 1
  yield undefined
  yield 3
}

const it1 = filterIterable(gen1(), n => n % 2 === 0)
const it2 = filterIterable(gen2(), nonNullable) // should be AsyncIterable<1 | 2>

我想出了这个界面:

interface FilterIterable {
  <T> (
    iter: AsyncIterable<T>,
    predicate: (val: T, index: number) => boolean,
  ): AsyncIterable<T>;
  <T, S extends T> (
    iter: AsyncIterable<T>,
    predicate: (val: T, index: number) => val is S,
  ): AsyncIterable<S>;
}

我可以将其应用于函数表达式,但显然不适用于如何将函数类型应用于函数声明的函数声明。这不是发电机吗?

typescript generator
1个回答
0
投票

因此您希望

filterIterable()
成为具有多个调用签名的 重载 函数。虽然您目前确实无法按照 microsoft/TypeScript#22063 中的要求直接注释函数,但您可以仅使用常规重载语法并在实现之前声明调用签名:

// call signatures
function filterIterable<T, S extends T>(
  iter: AsyncIterable<T>, predicate: (val: T, index: number) => val is S
): AsyncIterable<S>;
function filterIterable<T>(
  iter: AsyncIterable<T>, predicate: (val: T, index: number) => boolean
): AsyncIterable<T>;

// implementation
async function* filterIterable<T>(
  iter: AsyncIterable<T>, predicate: (val: T, index: number) => boolean
) {
  let i = 0
  for await (const val of iter) {
    if (predicate(val, i++)) {
      yield val
    }
  }
}

现在应该可以按您的预期工作:

async function foo() {
  const it1 = filterIterable(gen1(), n => n % 2 === 0)
  for await (const x of it1) {
    //             ^? const x: 1 | 2 | 3
    console.log(x.toFixed(2))
  }
  const it2 = filterIterable(gen2(), nonNullable) 
  for await (const y of it2) {
    //             ^? const y: 1 | 3
    console.log(y.toFixed(3))    
  }
}

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.