Search code examples
typescript

How to add an optional type using Pick utility type?


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
 delievered: boolean
 name: string
 surname: 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
}

Solution

  • 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