简体   繁体   中英

Type guards doesn't work when I use them with ReturnType in TypeScript

I have and enum and two functions that returns different data types:

enum Figure {
  Circle,
  Square,
}

const getCircle = () => ({
  figure: Figure.Circle,
  radius: 1
})

const getSquare = () => ({
  figure: Figure.Square,
  width: 1
})

I want to have a union type AnyFigure so that I can use circles and squares in combination. I can do so by defining both types:

type CircleType = {
  figure: Figure.Circle,
  radius: number
}

type SquareType = {
  figure: Figure.Square,
  width: number
}

type AnyFigure = CircleType | SquareType

// this throws compiler error because circles doesn't have width, which is great
const figure: AnyFigure = { figure: Figure.Circle, width: 4 }

This works fine, but I don't want to define return type of every "get figure" function (because in my actual code those are action creators that I use in React's useReducer hook, and there could be quite of few of them, each with different return type).

Thus I tried to use ReturnType instead:

type AnyFigureFromFuncs = ReturnType<typeof getCircle> | ReturnType<typeof getSquare>
// this doesn't throw compile errors, tested on TS 4.5.2
const figure: AnyFigureFromFuncs = { figure: Figure.Circle, width: 4 }

What am I missing here? Thanks.

You can just use as const on enum properties:

enum Figure {
    Circle,
    Square,
  }
  
  const getCircle = () => ({
    figure: Figure.Circle as const,
    radius: 1
  })
  
  const getSquare = () => ({
    figure: Figure.Square as const,
    width: 1
  })

  type AnyFigureFromFuncs = ReturnType<typeof getCircle> | ReturnType<typeof getSquare>
// this doesn't throw compile errors, tested on TS 4.5.2
const figure: AnyFigureFromFuncs = { figure: Figure.Circle, width: 4 }

You can use AnyFigure as return type.

const getCircle = (): AnyFigure => ({
  figure: Figure.Circle,
  radius: 1
})

Or the type itself.

const getCircle = (): CircleType => ({
  figure: Figure.Circle,
  radius: 1
})

Without your showing why you think explicit return types cause a problem with your reducers, it's difficult to answer this in a way that I feel confident satisfies your criteria. Reducers can definitely be quite complex, but sound return types are part of type safety, and can be important in discrimination. Complex, safe programs simply aren't easy to write.

As the question is written, it sounds like you're just asking " How can I make factory functions with one return type (but not have to annotate it every time)? " If that's the case, then you can just use a factory creator like this:

TS Playground link

function createFigureFactory (figure: AnyFigure): () => AnyFigure {
  return () => figure;
}

const getCircle = createFigureFactory({
  figure: Figure.Circle,
  radius: 1,
});

const getSquare = createFigureFactory({
  figure: Figure.Square,
  width: 1,
});

// this is AnyFigure
type AnyFigureFromFuncs = ReturnType<typeof getCircle> | ReturnType<typeof getSquare>;

const figure: AnyFigureFromFuncs = { figure: Figure.Circle, width: 4 };
/* Error:                                                   ^^^^^^^^
   Object literal may only specify known properties, and 'width' does not exist in type 'CircleType'.(2322) */

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM