It depends on the situation. So let's assume we have this very simple example, a React component called file size, and it has a util function format bytes. Now, if this is the only place where we use this util function, if we remove the component, then yes, TypeScript will tell us that we can safely remove this function if we have the no unused locals flag turned on, which I think it is on per default. But what happens if we change a tiny thing? What happens if we add an export statement in front of format bytes? Well, then TypeScript will not report any errors at all, because TypeScript simply does not do cross-module usage analysis. If something is exported, then TypeScript will just think that it will assume that it's used somewhere else, and it will not report any errors on it. And yeah, in this simple scenario, this might not be a problem, but in practice, what usually happens is that those two things tend to drift apart. Like the utils move to a separate file, maybe they become global, they become used somewhere else, and then usages get removed over time. And what happens is that what stays behind is this util function that nobody uses anymore. And I think that's the easiest and most common way to produce that code. And AI also does it all the time when they take one approach and then take another approach and leave a lot of that code behind.
Now, good IDEs can help with that. For example, IntelliJ creates an index of everything, and then they mark these things visually, but it's still up for the developers to act upon that. And I thought, you know, this can't be it. Like, surely someone else must have had the same problem and already built a tool for this, right? And it turns out there are actually a couple of tools that try to help with this. There are tools like tsprun or unimported, and they have existed for quite some time. But what they all have in common now is that they're either deprecated or unmaintained, and that they recommend KNIP by Lars Kuppert instead. So what is KNIP? Well, KNIP is a comprehensive tool to statically analyze your code for unused files, exports, and dependencies, and then report them to you so that you can remove them. It is quite fast. It only takes around six seconds in the quite large Sentry code base. It tries to be zero-config, even though in any larger repository you might need a bit of configuration. And it has a lot of plugins, support for monorepos, and is also auto-fixable.
Now that is a good sales pitch, but how does it actually work? So at its core, KNIP needs to know two things. It has to know which files are part of your project. And how complicated that is usually depends on your product structure, but it can be as easy as all your TypeScript files in your source directory. And the second thing is it has to know where the entry points to your application are. And what the entry points are, that drastically varies about which bundle or framework you're using. For example, if you're using Next.js, then every page.tsx or layout.tsx file in your app directory would have to be one of those entry points. But if you're just using Vite, for example, it might just be your main.tsx file. And once KNIP knows that, it will traverse the project, starting from the entry points, to build a graph of those used symbols. And it will then simply report on the things that are in the project, but not in that graph.
Comments