Как создать подтип функции в TypeScript?

Простой вопрос на первый взгляд. Но так ли это? Давайте разберемcя на примере.

Дано 2 типа для функций:

Кто чей подтип? У f1 тип аргумента уже, поэтому можно предположить, что f1 будет подтипом f2. Но, на самом деле нет. Если скомпилировать код ниже, то мы увидим, что isSubtype равен false:

На деле f2 будет являться подтипом f1, несмотря на то, что тип аргумента шире. И это не какая-то условность языка, а вполне логические объясняемое правило.

Если бы f1 являлся подтипом f2, то мы бы могли с f1 работать как с f2. Но у f2  тип аргумента шире, и в рантайме такое поведение приведет к ошибке. А вот с f2 можно смело обращаться как с f1.

С возвращаемыми типами обратная ситуация. В подтипе их надо сужать. Полностью правило звучит так:

Тип функции A будет являться подтипом базового типа B, если типы аргументов A будут являться контрвариантами типам аргументов B, а тип возвращаемого значения A будет являться ковариантом типа возвращаемого значения B.

Это правило работает только, если включен strict.

Подробнее можно почитать тут: https://dmitripavlutin.com/typescript-covariance-contravariance/