Search⌘ K
AI Features

Custom TypeScript PropertiesOfType Function

Explore how to build a custom PropertiesOfType generic type in TypeScript to limit sort operations to valid property types such as string, number, and Date. Understand how this improves type safety and prevents errors in generic sort functions when dealing with unsupported property types.

We'll cover the following...

We’ve built a powerful generic search function, capable of sorting in both ascending and descending fashion, but there is still an improvement that can be made. A problem arises from the fact that we use both the greater than (>) and less than (<) symbols in our genericSort function. These operators don’t make much sense when comparing JavaScript primitive types like Symbol, boolean, null, and so on. In fact, the list of types that should be allowed as property to genericSort is much smaller than those that shouldn’t.

Custom PropertiesOfType type

One way to restrict the type of property without having to write more code in genericSort is to create a custom type that only allows the types that make sense when compared with > and < operators. In this case, those are string, Date, and number types.

We’ll call this type PropertiesOfType. The PropertiesOfType will accept two generic types—Type, the type of which properties we’ll want to allow and the ValueTypes, all the types of the values allowed in Type.

Then, using the keyof operator, we can check the type of each value of Type. If the value of the property at that key extends our ValueTypes, we return the key type K. Otherwise, we return never, which removes that key from our list of allowed keys. The PropertiesOfType type looks like this:

TypeScript 3.3.4
export type PropertiesOfType<Type, ValueTypes> = {
[K in keyof Type]: Type[K] extends ValueTypes ? K : never;
}[keyof Type];

When included in our generic sort function, genericSort becomes:

TypeScript 3.3.4
import { PropertiesOfType } from './PropertiesOfType'
export const genericSort = <T>(a: T, b: T, property: PropertiesOfType<T, string | Date | number>, isDescending: boolean) => {
const result = () => {
if (a[property] > b[property]) {
return 1;
}
if (a[property] < b[property]) {
return -1;
}
return 0;
};
return isDescending ? result() * -1 : result();
}

This restricts which property we should be able to pass to genericSort. Unfortunately, our existing IWidget and IPerson interfaces are too limited in their functionality, but let’s imagine extending IWidget to include a function, another object, and a symbol property. The genericSort function will not allow these three types. Let’s try running this code and observing the errors generated by TypeScript.

Files
index.ts
PropertiesOfType.ts
widgets.ts
genericSort.ts
IWidget.ts
TypeScript 3.3.4
import { widgets } from "./widgets"
import { genericSort } from "./genericSort"
import { IWidget } from "./IWidget"
// These are not allowed!
widgets.sort((a, b) => genericSort<IWidget>(a, b, "myFunction", true))
widgets.sort((a, b) => genericSort(a, b, "myObject", true))
widgets.sort((a, b) => genericSort(a, b, "mySymbol", true))
// All fine! (Strings, dates, or numbers!)
widgets.sort((a, b) => genericSort(a, b, "title", true))
widgets.sort((a, b) => genericSort(a, b, "created", true))
widgets.sort((a, b) => genericSort(a, b, "rating", true))
PropertiesOfType won't let us sort on properties myFunction, myObject, or mySymbol