Rolf van de Krol
Rolf van de Krol

Reputation: 444

TypeScript extends keyword messing up type inference

Why is the following TypeScript code invalid?

type A = { kind: "a" }
type B = { kind: "b" }

const a = (a: A): void => undefined
const b = (b: B): void => undefined

const c = <C extends A | B>(c: C): void => (c.kind == "a" ? a(c) : b(c))

It looks like TypeScript cannot figure out that after c.kind == "a", c is an A. Why not?

The following variant seems to be working.

type A = { kind: "a" }
type B = { kind: "b" }
type C = A | B

const a = (a: A): void => undefined
const b = (b: B): void => undefined

const c = (c: C): void => (c.kind == "a" ? a(c) : b(c))

Upvotes: 2

Views: 51

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250056

A typeguard will work on a parameter of a union type, it will not work on a generic type parameter that extends a union. This is documented in this issue. The issue is open and might get fixed at a later date.

A possible work-around for this issue is to use your non-generic version, which for your simple example will work just the same.

If your signature is more complicated and you use the generic types in some other capacity (for example in some conditional types, or to create a relation between other parameter or parameters and return types) you can use a function with multiple signatures, a public generic signature and an non-generic implementation signature:

function c<C extends A | B>(c: C): void
function c(c: A | B): void {
    return c.kind == "a" ? a(c) : b(c);
}

Playground link

Upvotes: 2

Related Questions