Jacob Paris
← Back to all content

Filter an array in Typescript

I had a typed array and wanted to remove elements with an empty value. In vanilla Javascript that's easy enough, but instead of regular strings I was using string unions to restrict the available options.

ts
type Property = {
measurements: Measurement[]
}
type Measurement = {
type: MeasurementType
value: number
unit: MeasurementUnit
}
type MeasurementType =
| "LOT_DEPTH"
| "LOT_AREA"
| "LIVING_AREA"
type MeasurementUnit =
| "SQUARE_FEET"
| "SQUARE_METERS"
| "FEET"
| "METERS"
function getProperty(input): Property {
return {
measurements: [
{
type: "LOT_DEPTH",
value: input.lotDepth.value,
unit: input.lotDepth.unit,
},
{
type: "LOT_AREA",
value: input.lotArea.value,
unit: input.lotArea.unit,
},
{
type: "LIVING_AREA",
value: input.livingArea.value,
unit: input.livingArea.unit,
},
].filter((measurement) => measurement.value),
}
}

Typescript was able to correctly infer the type of the measurements array, but as soon as I added the filter it broke. Why? Filter returns a regular array of regular objects, and the type information about the string unions was lost

sh
Type 'string' is not assignable to type 'MeasurementName'

Solutions

The solution I ended up going with was to use type predicates to tell filter which type to use. I found out afterward that TS can infer correctly without the type predicate as long as it's split with an intermediate variable.

Type predicates

ts
.filter((measurement): measurement is Measurement => measurement.value)

Type predicates (JSDoc)

js
.filter(/** @type function(*): measurement is Measurement */ (measurement) => measurement.value)

Intermediate variable (JSDoc)

js
function getProperty(input): Property {
/** @type {Measurement[]} */
const measurements = [
{
type: "LOT_DEPTH",
value: input.lotDepth.value,
unit: input.lotDepth.unit,
},
{
type: "LOT_AREA",
value: input.lotArea.value,
unit: input.lotArea.unit,
},
{
type: "LIVING_AREA",
value: input.livingArea.value,
unit: input.livingArea.unit,
},
]
return {
measurements: measurements.filter(
(measurement) => measurement.value,
),
}
}
Professional headshot
Moulton
Moulton

Hey there! I'm a developer, designer, and digital nomad building cool things with Remix, and I'm also writing Moulton, the Remix Community Newsletter

About once per month, I send an email with:

  • New guides and tutorials
  • Upcoming talks, meetups, and events
  • Cool new libraries and packages
  • What's new in the latest versions of Remix

Stay up to date with everything in the Remix community by entering your email below.

Unsubscribe at any time.