Q:

mypy で Iterable[Optional[type]] から Iterable[type] を導出したい

今、 mypy を用いて開発を行なっています。
Iterable[Optional[type]] があったとき、これを Iterable[type] に変換する関数を記述しようと思い、次のコードを記述しました。
from typing import Optional, Iterable

def remove_none(iterable: Iterable[Optional[int]]) -> Iterable[int]:
    return filter(lambda x: x is not None, iterable)

しかし、これは以下のエラーになります。
test.py:5: error: Argument 2 to "filter" has incompatible type "Iterable[Optional[int]]"; expected "Iterable[int]"

質問
Iterable の中身から Optional を外すのは、割と一般的な行為だと思われますが、これは、 mypy でエラーにならないで実装するのは、一般的にどのように行われますか？

A:

地道な方法
地道に下の通り書くと型チェックを通りました。
def remove_none_iterator(it: Iterator[Optional[int]]) -> Iterator[int]:
    while True:
        try:
            elem = next(it)
            while elem is None:
                elem = next(it)
            yield elem
        except StopIteration:
            return

def remove_none(iterable: Iterable[Optional[int]]) -> Iterable[int]:
    it = iter(iterable)
    return remove_none_iterator(it)

ただしこれだと返り値が必ずイテレーターになってしまいます。しかしそもそも filter() 関数は iterable を受け取ってイテレーターを返すので、質問文のプログラムとは同じ挙動になるはずです。
Iterable 全体に対応するためにはもっと地道にクラスごとに場合分けすれば良さそうですが、そもそも Iterable 全体に対応する必要があるのか疑問です。たとえばタプルも Iterable ですが、タプルから None の要素を除いて別の長さのタプルにする操作はやや不自然に感じます。
無視する方法
このくらい自明な処理だと、cast() や # type: ignore を使って型チェックを無視させてしまうというのも手です。
def remove_none(it: Iterable[Optional[int]]) -> Iterable[int]:
    return cast(Iterable[int], filter(lambda x: x is not None, it))

# または

def remove_none(it: Iterable[Optional[int]]) -> Iterable[int]:
    return filter(lambda x: x is not None, it)  # type: ignore  # https://ja.stackoverflow.com/q/52067

前者は remove_none() の型と合うかはチェックされ、後者は単に無視されます。ただし前者も実行時のチェックはされません。
文字通り型チェックが無視されるので、人間のミスに気を付けなければいけません。PEP 484 "Type Hints" には以下のように書かれているので、プロジェクトにとって良い塩梅を見極めるのが良さそうです。

It should also be emphasized that Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.

誤った方法
型チェックに通ったからと言って完全に安心できるわけではありません。たとえば Python 組み込み関数 filter() は、フィルター用関数に None を指定すると false-y な要素を除去する挙動をします。

function が None なら、恒等関数を仮定します。すなわち、iterable の偽である要素がすべて除去されます。

mypy においてもこの挙動を反映していて、フィルター用関数が None かそうでないかで filter の型を切り替えています。

filter(None, it: Iterable[Optional[_T]]) -> Iterator[_T]
filter(f: Callable[[_T], Any], it: Iterable[_T]) -> Iterator[_T]

型だけ見ると第一引数を None にすれば良さそうに見えますが、このとき 0 や False などもフィルタリングされてしまいます。
def remove_none(iterable: Iterable[Optional[int]]) -> Iterable[int]:
    return filter(None, iterable)

list(remove_none([0, 1, 2, None]))  # ==> [1, 2]

フィルター用関数が None でない場合は Iterable[_T] から Iterator[_T] を作る形になるので、Iterable[Optional[_T]] を受け取っても Iterable[Optional[_T]] しか返せません。関数型プログラミング言語でよく使われている filter 関数の型からすればこちらの挙動の方が自然で、型を変えるには map を使う発想になるでしょう。

