alkam.in

Named parameters and the `Partial` type

cover
Photo by Jordan M. Lomibao on Unsplash

While doing some work around default URL parameters we found ourselves wanting a way to implement a function that acted similarly to a python function with named parameters. We wanted to be able to pass in one or all of the parameters and if we did not provide the parameter, we wanted a default value to be used. We also wanted our implementation to be consistent within the type-system. We settled on the following implementation:

type UrlParams = {
  page: number;
  sort: "asc" | "desc";
  search: string;
};

type OptionalUrlParams = {
  page?: number;
  sort?: "asc" | "desc";
  search?: string;
};

const sanitizedParams = getSanitizedParams();

const updateUrlParameters = ({
  page = sanitizedParams.page,
  sort = sanitizedParams.sort,
  search = sanitizedParams.search
}: OptionalUrlParams) => dispatch(push(`list/${page}/${sort}/${search}`));

In order to allow the consumer of updateUrlParameters to pass in one or all of the parameters, we manually created a version of UrlParams that made every property optional.

We could use this with a call like updateUrlParameters({ page: 1 }). The OptionalUrlParams type declaration bothered me for sure, but it seemed like the best approach given the constraints.

Partial to the rescue

Not even 24 hours later, while browsing the TypeScript docs (that's a normal thing to do -- thank you), I noticed the Partial utility type.

Constructs a type with all properties of T set to optional. This utility will return a type that represents all subsets of a given type.

I hadn't been looking for a way to clean up the implementation but it seemed ungrateful to the powers that be not to give it a try. After tossing that OptionalUrlParams type aside and dropping in Partial we had something that looked healthier and functioned the same.

type UrlParams = {
  page: number;
  sort: "asc" | "desc";
  search: string;
};

const sanitizedParams = getSanitizedParams();

const updateUrlParameters = ({
  page = sanitizedParams.page,
  sort = sanitizedParams.sort,
  search = sanitizedParams.search
}: Partial<UrlParams>) => dispatch(push(`list/${page}/${sort}/${search}`));

Now, if we needed to alter the URL parameters in any way we wouldn't have to ensure we also updated the optional version. I haven't been working in TypeScript long but I've learned that unless you lock things down,

Happily ever after

Finding Partial was a bit of a relief on a few levels:

  1. I'm not the only one who's ever wanted to make every property of a type optional
  2. There's a way to do it without having to create a new type declaration
github profile
The personal blog of Alex Kaminsky
I work at Azavea where we build geospatial apps for the web.