Chet
Chet

Reputation: 19889

Typescript function overloads not working for the union type case

I'm having trouble getting function overloads to work properly in Typescript.

I have a simple union type and a function that can handle either type. I've created overloads to handle each of them independently.

type A = { type: "a", x: number }
type B = { type: "b", x: number, y: number }

type Z = A | B

function f(z: A): { x: number, y: undefined }
function f(z: B): {x: number, y: number}
function f(z: Z) {
    if (z.type === "a") {
        return {x: z.x, y: undefined}
    } else {
        return {x: z.x, y: z.y}
    }
}

It appears to work as well.

// This works
const a = f({ type: "a", x: 1 })
const b = f({ type: "b", x: 1, y: 1 })

However, when it comes to using it with a disambiguated union type, it doesn't work.

// Why doesn't this work?
function g(z: Z) {
    const x = f(z) 
}

// This also doesn't work.
function h<T extends Z>(z: T) {
    const x = f(z) 
}

I get this error:

Argument of type 'Z' is not assignable to parameter of type 'B'.
  Type 'A' is not assignable to type 'B'.
    Property 'y' is missing in type 'A'.

Seems like it might user error, but also seems kind of like a bug...

Here's a playground to check try it yourself. Make sure to turn strictNullChecks on!

Upvotes: 3

Views: 651

Answers (1)

artem
artem

Reputation: 51759

Overloaded function implementation is not taken into account when compiler does overload resolution. You have to add an explicit overload declaration for Z argument type, for exapmle like this:

function f(z: Z): {x: number, y: number | undefined};

The complete code is

type A = { type: "a", x: number }
type B = { type: "b", x: number, y: number }

type Z = A | B

function f(z: A): { x: number, y: undefined }
function f(z: B): { x: number, y: number}
function f(z: Z): { x: number, y: number | undefined}
function f(z: Z) {
    if (z.type === "a") {
        return {x: z.x, y: undefined}
    } else {
        return {x: z.x, y: z.y}
    }
}

const a = f({ type: "a", x: 1 })
const b = f({ type: "b", x: 1, y: 1 })


function g(z: Z) {
    const x = f(z) 
}

function h<T extends Z>(z: T) {
    const x = f(z) 
}

Upvotes: 2

Related Questions