Where TypeScript Will Fail You

Caleb LeNoir
Geek Culture
Published in
3 min readJun 7, 2021
Photo by Gavin Allanwood on Unsplash

TypeScript is great. I made the switch a few years ago when I was starting on a new project. I haven’t looked back since. I feel much more confident in the code that I produce, that it will work (mostly) as I expect it to. Sometimes, that confidence can come back to bite me.

Today, I was adding some functionality to a clearFilters function we have. It looks something like this:

const [activeFilters, setActiveFilters] = useState<Filter[]>(initialFilters)const clearFilters = () => {
setActiveFilters([])
}

Pretty simple. We have a filtering system and this resets the filters. It’s used in our Filters component and also as a shared function so we can clear filters outside of the Filters component when we need to.

In Filters, it looks something like this:

interface Props {
filters: Filter[]
clearFilters: () => void
}
const Filters: React.FC<Props> = ({
return (
<div>
... the filters
<button
onClick={clearFilters}
>
Clear All
</button>
</div>
)
})

Again, pretty simple. Clicking the Clear All button clears all the filters, which triggers a new query to the API in a higher level component. Now, what if I want to make it more complicated? I want to be able to clear all the filters except a few that I want to remain.

Great! Let’s update our clearFilters function

const clearFilters = (except?: FilterType[]) => {
if(except){
setActiveFilters(activeFilters.filter((f) => except.includes(f.type))
} else {
setActiveFilters([])
}
}

We’ve updated the library. Everything compiles. No TypeScript errors! Hurrah! Push it to Prod!

Except

Photo by Conor Samuel on Unsplash

I’m getting an exception every time I try to clear filters that says except.includes is not a function! What the what?!

I thought I really gotten one over on TypeScript with this error. Turns out, it’s a pretty simple mistake. But as with all mistakes, it’s only simple once you have figured it out.

My (Simple?) Mistake

Let’s look at Filters again.

interface Props {
filters: Filter[]
clearFilters: () => void
}
const Filters: React.FC<Props> = ({
return (
<div>
... the filters
<button
onClick={clearFilters}
>
Clear All
</button>
</div>
)
})

See it? My Props are no longer valid. I need to update them to reflect the new clearFilters function.

interface Props {
filters: Filter[]
clearFilters: (except?: FilterType[]) => void
}

Once I’ve done that, my onClick={clearFilters} line is going to throw a TypeScript compile error. The types don’t match! Setting onClick to the function without any parameters means the mouse event from the click is going to get passed to the function.

To fix that, we make another small update:

<button onClick={() => clearFilters()}>All Clear</button>

Now we are actually all in the clear and everything is working as it should!

Lessons Learned

  1. When passing a function as a prop, update the type when you update the function

TypeScript is great, but it will only do what I tell it to do. If I had been importing the clearFilters function directly there wouldn’t have been a problem here. Because I was passing it in as a parameter, I needed to make sure the prop type matched the function I was passing in.

2. Write automated tests

In this case, my error was caught by our Cypress tests on the build server. If not for that, it’s possible this code could have released into production without anyone realizing it was broken. These automated tests prevented major headaches, especially ahead of a deploy late on a Friday.

Hope my mistakes can save you from some in the future! Where have you run into things with TypeScript that happened differently than you expected? What lessons have you learned along the way?

--

--