Home > Software design >  Return type based on parameter value
Return type based on parameter value

Time:01-12

My goal is to have different type for different parameter type

Here is my code

import React from "react";

type Response<T> = {
  data: T;
  description: string;
  date: string;
};
type Data = {
  title: string;
  name: string;
};
type DataWidthLabel = Data & { labels: string };
type Endpoint = "endpoint_1" | "endpoint_2";

type FnResponse<T> = 
  T extends "endpoint_1" ? Response<Data> :
  T extends "endpoint_2" ? Response<DataWidthLabel> : never;

type Props = {
  endpoint: Endpoint;
  children: (data: Response<Data> | Response<DataWidthLabel>) => void;
};

function queryFunction<T extends Endpoint>(endpoint: T): FnResponse<T> {
  if (endpoint === "endpoint_1") {
    return {
      data: {
        title: "title",
        name: "name"
      },
      description: "description",
      date: "date"
    } as FnResponse<T>;
  }

  return {
    data: {
      title: "title",
      name: "name",
      labels: "labels"
    },
    description: "description",
    date: "date"
  } as FnResponse<T>;
}

const DataComp: React.FC<Props> = ({ endpoint, children }) => {
  // queryFunction takes a type argument (<myType>) to type the data response
  const data = queryFunction(endpoint);

  return (
    <div>
      {children(data)}

      <div>
        <p>{data.description}</p>
        <p>{data.date}</p>
      </div>
    </div>
  );
};

const ParentComp1 = () => (
  <DataComp endpoint="endpoint_1">
    {(data) => {
      return data.data.name;
    }}
  </DataComp>
);

const ParentComp2 = () => (
  <DataComp endpoint="endpoint_2">
    {(data) => {
      return data.data.labels;
    }}
  </DataComp>
);

export default function App() {
  return (
    <div className="App">
      <ParentComp1 />
      -----
      <ParentComp2 />
    </div>
  );
}

I'm not able to fix error for the case return data.data.labels;

CodePudding user response:

If you make Props generic, then you can say that the parameter in data must be the FnResponse of the endpoint field:

type Props<E extends Endpoint> = {
  endpoint: E;
  children: (data: FnResponse<E>) => void;
};

Also, you could change FnResponse to a lookup table instead:

type FnResponse<T extends string> = Response<({
    "endpoint_1": Data;
    "endpoint_2": DataWidthLabel;
} & { [key: string]: never })[T]>;

Lastly, the component itself needs to be generic so we can pass down the generic parameter to Props:

function DataComp<E extends Endpoint>({ endpoint, children }: Props<E>) {

or

// NO React.FC!
const DataComp = <E extends Endpoint>({ endpoint, children }: Props<E>) => {

Playground

  • Related