0

I want to type an Express request handler to enforce that the response body has one of two types. While doing this, I discovered that Express interprets the following ways of typing the response body differently:

interface Foo {
    a: number;
}
interface Bar {
    b: string;
}

let foo = { a: 42 };
let bar = { b: "abc" };
let foobar = { ...foo, ...bar };

// union inside generic
app.get("/foobar1", async (req, res: Response<Foo | Bar>) => {
    // all of these type check
    res.json(foo);
    res.json(bar);
    res.json(foobar);
});

// union outside generic
app.get("/foobar2", async (req, res: Response<Foo> | Response<Bar>) => {
    // these first two don't type check
    res.json(foo);
    res.json(bar);
    // only this type checks
    res.json(foobar);
});

When the union is inside the generic argument, the type checking behaves as I expect - if I send a response body that contains either of my body types, it compiles. But when the union is outside the generic argument, Express converts the body type into Foo & Bar, so only sending a Foo or only sending a Bar gets a type error.

This seems to be an Express-specific thing, because if I try to reproduce this on a simpler interface with a generic argument, I don't get any type errors:

interface Wrapper<T> {
    data: T;
}
type Outside = Wrapper<Foo> | Wrapper<Bar>;
type Inside = Wrapper<Foo | Bar>;

let foo = { a: 42 };
let bar = { b: "abc" };
let foobar = { ...foo, ...bar };

// all of the below compiles
let baaz1: Outside = { data: foo };
let baaz2: Inside = { data: foo };
let baaz3: Outside = { data: foobar };
let baaz4: Inside = { data: foobar };

While I can simply put the union inside the generic argument, I was curious why this happens. Why does Response<Foo | Bar> type response bodies as Foo | Bar, but Response<Foo> | Response<Bar> type responses bodies as Foo & Bar?

Galen Long
  • 3,693
  • 1
  • 25
  • 37
  • 1
    Actually, it's because of [**variance**](https://stackoverflow.com/questions/66410115/difference-between-variance-covariance-contravariance-and-bivariance-in-typesc). – kelsny Jan 25 '23 at 20:38
  • @vera. Oh, that's extremely interesting! Thank you. – Galen Long Jan 25 '23 at 20:59

0 Answers0