Home > Enterprise >  TypeScript can not recognise return type correctly
TypeScript can not recognise return type correctly

Time:02-11

I'm trying to define a conditional type for the return value and I've faced with weird TS behavior. However, the function return object has the same shape as defined in conditional types the TS error occurred.

My dummy (example) code:

interface Order1 {
  id: number;
  consumerId: number,
  articleId: number,
  lastDate: string
}

interface Order2 {
  id: number;
  consumerId: number,
  articleId: number,
  firtsDate: string
}

type Article = {
  name: number;
  status: number;
  articleTypeId: number;
  description: Array<string>;
  id: number,
}

interface NormalizedOrder1 extends Omit<Order1, 'articleId'> {
  article: Article | undefined;
  consumerName: string | undefined,
}

interface NormalizedOrder2 extends Omit<Order2, 'articleId'> {
  article: Article | undefined;
  consumerName: string | undefined,
}

type ReturnType<T> = T extends Order1
  ? NormalizedOrder1[]
  : T extends Order2
    ? NormalizedOrder2[]
    : never;

export default function useNormalizedOrders<T extends Order1 | Order2>(
  orders: T[],
  articles: Article[]
): ReturnType<T> {
  const consumers = [{id: 1, name: 'Consumer'}]

  return orders.map(
        ({ articleId, consumerId, ...rest }) => ({
          ...rest,
          consumerId,
          article: articles.find((article) => article.id === articleId),
          consumerName: consumers?.find(({ id }) => id === consumerId)?.name,
        })
      )
}

Error:

Type '(Omit<T, "articleId" | "consumerId"> & { consumerId: number; article: Article | undefined; consumerName: string | undefined; })[]' is not assignable to type 'ReturnType<T>'.(2322)

Link to TS playground

TS version: 4.5.4

Maybe someone could explain to me what I'm doing wrong and how to reach expected behavior? Thank you!

CodePudding user response:

TypeScript does not support conditional types in a return type place. AFAIK, you have two options.

  1. Overloading
interface Order1 {
  id: number;
  consumerId: number,
  articleId: number,
  lastDate: string
}

interface Order2 {
  id: number;
  consumerId: number,
  articleId: number,
  firtsDate: string
}

type Article = {
  name: number;
  status: number;
  articleTypeId: number;
  description: Array<string>;
  id: number,
}

interface NormalizedOrder1
  extends Omit<Order1, 'articleId'> {
  article: Article | undefined;
  consumerName: string | undefined,
}

interface NormalizedOrder2
  extends Omit<Order2, 'articleId'> {
  article: Article | undefined;
  consumerName: string | undefined,
}

type ReturnType<T> = T extends Order1
  ? NormalizedOrder1[]
  : T extends Order2
  ? NormalizedOrder2[]
  : never;

export default function useNormalizedOrders<
  T extends Order1 | Order2
>(orders: T[], articles: Article[]): ReturnType<T>
export default function useNormalizedOrders<
  T extends Order1 | Order2
>(orders: T[], articles: Article[]) {
  const consumers = [{ id: 1, name: 'Consumer' }]

  return orders.map(
    ({ articleId, consumerId, ...rest }) => ({
      ...rest,
      consumerId,
      article: articles.find((article) => article.id === articleId),
      consumerName: consumers?.find(({ id }) => id === consumerId)?.name,
    })
  )
}

Playground

  1. Type assertion
export default function useNormalizedOrders<
  T extends Order1 | Order2
>(orders: T[], articles: Article[]) {
  const consumers = [{ id: 1, name: 'Consumer' }]

  return orders.map(
    ({ articleId, consumerId, ...rest }) => ({
      ...rest,
      consumerId,
      article: articles.find((article) => article.id === articleId),
      consumerName: consumers?.find(({ id }) => id === consumerId)?.name,
    })
  ) as any as ReturnType<T> // type assertion
}
  • Related