Skip to content

Union of subtypes should be usable as the supertype #38048

@wojpawlik

Description

@wojpawlik

TypeScript Version: 3.8.3 and v3.9.0-dev.20200418

Search Terms:
common type subtype union

Code

interface Cata<OkIn, OkOut, ErrIn, ErrOut> {
    Ok(value: OkIn): OkOut
    Err(err: ErrIn): ErrOut
}

type CataOk<OkIn, OkOut> = Cata<OkIn, OkOut, never, unknown>
type CataErr<ErrIn, ErrOut> = Cata<never, unknown, ErrIn, ErrOut>

interface BaseResult<T, E> {
    readonly ok: boolean
    cata<OkOut, ErrOut>(cata: Cata<T, OkOut, E, ErrOut>): OkOut | ErrOut
}
export class Ok<T> implements BaseResult<T, never> {
    readonly ok = true as const
    constructor(readonly value: T) {}
    cata<OkOut>(cata: CataOk<T, OkOut>) {
        return cata.Ok(this.value)
    }
}
export class Err<E> implements BaseResult<never, E> {
    readonly ok = false as const
    constructor(readonly error: E) {}
    cata<ErrOut>(cata: CataErr<E, ErrOut>) {
        return cata.Err(this.error)
    }
}

export type Result<T, E> = Ok<T> | Err<E>

// tests

declare const u: Result<number, Error>

u.cata({
    Ok(v) { return '2' },
    Err() { return 3 },
})

Expected behavior:

No error (Liskov substitution principle).

Actual behavior:

This expression is not callable.
Each member of the union type (<OkOut>(cata: CataOk<number, OkOut>) => OkOut) | (<ErrOut>(cata: CataErr<Error, ErrOut>) => ErrOut) has signatures, but none of those signatures are compatible with each other. ts(2349)

Workaround:

export type Result<T, E> = BaseResult<T, E> & (Ok<T> | Err<E>)

Playground Link

Related Issues: #37704.

Metadata

Metadata

Assignees

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions