понять почему верхний пример не работает, а нижний спокойно работает
Разница в том, что в верхнем примере интерфейс Rule<TItem> зависит от своего типа-параметра TItem и ковариантно, и контрвариантно (поскольку ковариантно и контрвариантно зависит от T тип React.ComponentType<T>, через который проходит параметр TItem в Rule<TItem>). Ковариантная зависимость G<T> от T означает, что из S extends T (тип S является подтипом T) следует G<S> extends G<T> (для любых допустимых S и T). Контрвариантная зависимость G<T> от T означает, что из S extends T следует G<T> extends G<S>. Таким образом, функция iterate из верхнего примера, принимая тип-параметр TItem, будет требовать, чтобы её второй аргумент был массивом элементов Rule<T>, где одновременно T extends TItem (из-за ковариантности), и TItem extends T (из-за контрвариантности), то есть типы T и TItem фактически должны совпадать (в то время как TItem = {a: number, b: string}, а T = {a: number}, и TS фиксирует ошибку). В нижнем же примере интерфейс Rule1<TItem> зависит от TItem только контрвариантно (так как TItem входит в интерфейс только на позиции аргумента одного из методов), поэтому функция iterate1, принимая тип-параметр TItem, будет требовать лишь, чтобы её второй аргумент был массивом элементов Rule1<T>, где TItem extends T (что и выполняется при TItem = {a: number, b: string} и T = {a: number}). Тут можно посмотреть цепочки проверок типов в верхнем и нижнем примерах подробнее: play.
Обсуждают сегодня