TypeScript compiler wrapper for static analysis and code manipulation.
ts-morph is a TypeScript Compiler API wrapper that simplifies working with Abstract Syntax Trees (AST) for static analysis, code generation, and large-scale refactoring. Instead of navigating the verbose and complex compiler API directly, ts-morph provides an intuitive interface to load source files into an in-memory Project, query declarations, and modify code structures programmatically.
The package was created to address the steep learning curve of the raw TypeScript Compiler API, which requires extensive knowledge of node types, symbol resolution, and type checking internals. With over 8.8 million weekly downloads, ts-morph has become the standard tool for build systems, codemod authors, and developers maintaining type-safe tooling across large TypeScript codebases.
Typical users include framework authors generating type definitions, teams automating refactors across hundreds of files, and tooling developers building linters or migration scripts. The library operates entirely in memory until you explicitly save changes, reducing the risk of corrupting source files during complex transformations. It supports both TypeScript and JavaScript files, with optional type checking integration.
Originally named ts-simple-ast, the project evolved into ts-morph with a focus on developer experience. It includes companion packages like @ts-morph/bootstrap for lightweight compiler setup and ts-morph-structures for advanced node construction, forming an ecosystem for TypeScript metaprogramming.
import { Project, SyntaxKind } from 'ts-morph';
const project = new Project({
tsConfigFilePath: 'tsconfig.json',
});
const sourceFile = project.addSourceFileAtPath('src/user.ts');
const classes = sourceFile.getClasses();
classes.forEach(cls => {
console.log(`Found class: ${cls.getName()}`);
cls.getProperties().forEach(prop => {
if (!prop.hasModifier(SyntaxKind.PrivateKeyword)) {
prop.setIsReadonly(true);
console.log(` Made ${prop.getName()} readonly`);
}
});
const hasConstructor = cls.getConstructors().length > 0;
if (!hasConstructor) {
cls.addConstructor({
parameters: [{ name: 'id', type: 'string' }],
statements: 'this.id = id;'
});
cls.addProperty({ name: 'id', type: 'string', isReadonly: true });
}
});
const interfaceDecl = sourceFile.addInterface({
name: 'IUser',
isExported: true,
properties: classes[0]?.getProperties().map(p => ({
name: p.getName(),
type: p.getType().getText()
})) || []
});
sourceFile.saveSync();
console.log(`Generated interface ${interfaceDecl.getName()}`);Automated code refactoring: Rename classes, methods, or interfaces across an entire codebase while preserving type relationships. For example, converting all class properties to readonly or migrating from CommonJS to ES modules by rewriting import/export statements.
Type definition extraction: Generate shared type definitions from backend code for frontend consumption. A common pattern is extracting TypeScript interfaces from NestJS DTOs or Express route handlers to keep API contracts synchronized between client and server.
Custom linting and validation: Build project-specific rules that enforce architectural patterns, such as ensuring all public API methods have JSDoc comments or verifying that certain decorators are only used in specific directories.
Code generation from templates: Scaffold boilerplate code based on existing patterns, like generating CRUD operations from database entity definitions or creating test stubs that match actual implementation signatures.
Migration scripts: Automate breaking change migrations when upgrading dependencies. For instance, rewriting deprecated API calls, updating import paths after package restructuring, or converting legacy decorators to modern syntax.
npm install ts-morphpnpm add ts-morphbun add ts-morph