8000 Function-like recursive generic interface invariant in its parameter type · Issue #35805 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content
Function-like recursive generic interface invariant in its parameter type #35805
Closed
@jcalz

Description

@jcalz

I'm aware that this is likely to be a design limitation; I can't find an existing issue about this specific problem so I'm filing this one for reference.

TypeScript Version: 3.8.0-dev.20191220

Search Terms: variance, composition

Code

interface Fn<A, B> {
    (a: A): B;
    then<C>(next: Fn<B, C>): Fn<A, C>;
}

declare const fn: Fn<string, number>;
const contravariantInA: Fn<"a", number> = fn; // error!
// Type 'Fn<string, number>' is not assignable to type 'Fn<"a", number>'.
// Type 'string' is not assignable to type '"a"'.

const covariantInB: Fn<string, unknown> = fn; // okay

Expected behavior: Fn<A, B> should be contravariant in A.

Actual behavior: Fn<A, B> is invariant in A.

I came upon this while investigating a Stack Overflow question in which someone had built a composable type like Fn<A, B> and had to use a workaround to allow the then() method to accept something with a wider input.

@jack-williams suggests that the issue happens because the checker defaults to covariance for A when checking it (in typeArgumentsRelatedTo())... and combined with its contravariant use in the call signature, this results in invariance.

A full structural check might not have this problem but I imagine it might be hard to guarantee that such a check would terminate? (e.g., Fn<A, B> depends on Fn<B, C> which depends on Fn<C, D> which depends on Fn<D, E> etc?)

If there's no plausible way for the compiler to correctly infer the variance here, perhaps this is just another request for variance annotations as per #1394?

Not sure. Anyway, thanks!

Playground Link: Provided

Related Issues:
#1394: let us mark the parameters as covariant/contravariant/invariant ourselves
#32674: a different variance issue
#33872: a different variance bug

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions

    0