Skip to content

Expected error in generic class types used in recursive reference #37627

Closed
@dimitryvolkov

Description

@dimitryvolkov

TypeScript Version: 3.8.0-dev.20200118

Search Terms:
variance contravariance covariance
Code

class Success<T, E> {
    constructor(readonly value: T) { };
    public isSuccess(): this is Failure<T, E> {
        return true;
    }
}

class Failure<T, E>  {
    constructor(readonly error: E) { };
    public isSuccess(): this is Success<T, E> {
        return false;
    }
}

type Result<T, E> = Success<T, E> | Failure<T, E>;

function Test(n: Number): Result<number, Error> {
    return new Success("test");
}

Expected behavior:
Expected error:

Type 'Success<string, Error>' is not assignable to type 'Result<number, Error>'.
  Type 'Success<string, Error>' is not assignable to type 'Success<number, Error>'.
    Type 'string' is not assignable to type 'number'

Actual behavior:
No error

The code is part of an attempt to create a result object pattern in TS.
Through some bisection I have managed to narrow this down to 1a10e71

It appears this was considered an error up to that commit.
I have tried my luck in understanding the that PR but at the moment sadly I am unable to fully understand it
.
I was able to make the code correctly (assuming it is correct behavior) display the error by referencing both T and E in Success and Error respectively a-la:

class Success<T, E> {
    private _: E;
    constructor(readonly value: T) { };
    public isSuccess(): this is Failure<T, E> {
        return true;
    }
}

class Failure<T, E>  {
    private _: T;
    constructor(readonly error: E) { };
    public isSuccess(): this is Success<T, E> {
        return false;
    }
}

Trying to read the PR I assumed this was some how related to recursiveness of the types but again fully understanding it alludes me.

Another thing that alludes me is that if add the following code (which I understand is an error on its own):

declare class Success<T,E> {
    constructor(readonly value: T);
}

The expected behavior seemed to occur, I attempted this after being baffled as to why using an existing behavior (https://github.com/gDelgado14/neverthrow) yielded the expected behavior and my attempts at copying the library's code verbatim into my code did not.

If this indeed intended behavior I would love nothing more than to understand why is it so and how the aforementioned commit lead to it as I failed to deduce it on my own

Playground Link: Playground Link

Related Issues: #37400

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions