I'm looking for a way to calculate what parts of my object types are actually in use throughout my codebase. Let's say I have the following in a types.ts
file:
export type Animal = {
id: number;
name: string;
height: number;
isMammal: boolean;
}
And in another file I make an API call that returns an Animal:
const getAnimal: (id: number) => Promise<Animal>
And in two separate files, I call getAnimal
and use the resulting Animal
.
const animal1 = await getAnimal(1);
console.log(`Hi! Animal with id ${animal1.id} is named ${animal1.name}.`)
const animal1 = await getAnimal(1);
console.log(`This animal is ${animal1.height} feet tall.`)
But nowhere in my project do I use the isMammal
property. How can I write a script that scans all of the files in my project and tells me that isMammal
is not in use on the Animal
type? Or alternatively, that only id
, name
, and height
are?
To do this with the compiler API, you will want to use and setup a ts.LanguageServer
via ts.createLanguageService(...)
then use the findReferences
method, which returns back all the references used similar to what occurs in an editor.
Here's a self contained example:
// setup code because doing this with the compiler API alone is a lot to show
import { createProjectSync, ts } from "https://deno.land/x/ts_morph@15.1.0/bootstrap/mod.ts";
const project = createProjectSync();
const sourceFile = project.createSourceFile("file.ts", `
export type Animal = {
id: number;
name: string;
height: number;
isMammal: boolean;
};
const animal: Animal = {} as any;
animal.id;
animal.name;
animal.height;
`);
const languageService = project.getLanguageService();
// compiler API code starts here
const animalType = sourceFile.statements[0] as ts.TypeAliasDeclaration;
const animalObjectType = animalType.type as ts.TypeLiteralNode;
for (const member of animalObjectType.members) {
if (ts.isPropertySignature(member)) {
const findResult = languageService.findReferences(
sourceFile.fileName,
member.name.getStart(sourceFile),
);
const references = (findResult ?? [])
.reduce<ts.ReferencedSymbolEntry[]>((a, b) => a.concat(b.references), [])
.filter(r => !r.isDefinition);
if (references.length === 0) {
console.log(member.name.getText(sourceFile));
}
}
}
Outputs:
isMammal