Home > Back-end >  How to add an optional type using Pick utility type?
How to add an optional type using Pick utility type?

Time:12-27

I have a component for making HTTP requests, and one of the properties ('address') should be optional, since I don't need it in one of the components where I call that function.

The way I did it before is to use the Partial utility type, but I want to be more specific and make a use of Pick.

Old version:

export type Trip = {
 address: string
 product: string
 no: number
 customer: string
}


export const usePostTrip = () => {
  const queryClient = useQueryClient()
  const mutation = useMutation(
    async (data: Partial<Trip>) => {  // This is achieved with Partial
      const url = `${BASE_URL}/trip`
      return axios.post(url, data ).catch((e) => console.log(e.message))
    },
  )

  return mutation
}

Here is my current solution, which I believe is not the best one. Is there a better solution for this case?

export type Trip = {
 address: string
 product: string
 no: number
 customer: string
}

type tripWithoutAddress = Pick<
  Trip, 'product'| 'no'
>

type tripsWithAddress = Pick<
  Trip, 'product'| 'no' | 'address'
>

export const usePostTrip = () => {
  const queryClient = useQueryClient()

  const mutation = useMutation(
    async (data: tripWithoutAddress | tripsWithAddress) => {   // This is achieved with Pick
      const url = `${BASE_URL}/trip`
      return axios.post(url, data).catch((e) => console.log(e.message))
    },
  )

  return mutation
}

CodePudding user response:

It's not clearly stated in your question, but it sounds like you want a version of Trip where only the address property is optional, and the others remain required. This can be achieved by omitting the address property and then adding it back with an optional modifier by using an intersection type.

export type Trip = {
  address: string;
  product: string;
  no: number;
  customer: string;
};

export type TripWithOptionalAddress = Omit<Trip, "address"> & { address?: string; };

// All fields are required.
const trip: Trip = {
  address: "123 TypeScript Ave",
  product: "Stack Overflow",
  no: 123,
  customer: "Greg",
};

// `address` is missing.
const tripWithoutAddress: TripWithOptionalAddress = {
  product: "Stack Overflow",
  no: 123,
  customer: "Greg",
};

// `address` is still allowed, if present.
const tripWithAddress: TripWithOptionalAddress = trip;

CodePudding user response:

I think Jimmy's answer is good, but:

If you ever change the address property on Trip (e.g. from string to number), you'll also have to manually change the optional version in order to maintain type consistency.

By using Pick and Partial, you can parametrically derive the type from Trip: any changes to the address property on Trip will automatically be changed in the derived type, too:

TS Playground

type Trip = {
 address: string;
 customer: string;
 no: number;
 product: string;
}

type TripWithOptionalAddress = Omit<Trip, 'address'> & Partial<Pick<Trip, 'address'>>;

declare const t: TripWithOptionalAddress;
t.address // string | undefined
  • Related