TypeScript AST Wrapper and Code Manipulator
ts-morph (formerly ts-simple-ast) is a robust library that wraps the TypeScript Compiler API, offering a more user-friendly and programmatic way to navigate, analyze, and manipulate TypeScript and JavaScript code. It provides an in-memory file system where all changes are tracked until explicitly saved, allowing for complex refactoring and code generation tasks. The library maintains strong compatibility with recent TypeScript versions, often releasing new major versions shortly after a new TypeScript compiler release. The current stable version is 28.0.0, which supports TypeScript 6.0. Key differentiators include its extensive wrapper API, enabling easy traversal and modification of AST nodes, and its ability to fall back to the raw `compilerNode` when advanced compiler API access is needed, providing full flexibility for complex scenarios.
Common errors
-
Cannot find module 'ts-morph'
cause The package is not installed, or there's a mismatch between CommonJS `require` and ES module `import` syntax, or an incorrect import path.fixEnsure `ts-morph` is installed (`npm install ts-morph`). If using CommonJS, use `const { Project } = require('ts-morph');`. If using ES modules (recommended), ensure `"type": "module"` is set in `package.json` or use `.mjs` files. -
Error: You are using TypeScript X.Y.Z, but ts-morph was built with TypeScript A.B.C.
cause The installed version of `ts-morph` has a peer dependency on a specific TypeScript version, and your project's TypeScript version does not match.fixAdjust your project's `typescript` dependency in `package.json` to match the version required by `ts-morph` (check ts-morph's `package.json` or documentation). Then run `npm install`. -
InvalidOperationError: The operation is invalid for a node that has been forgotten.
cause Attempting to interact with an AST node that has been 'forgotten' (removed from the project's internal AST representation), typically after a manipulation that invalidates the node's position or existence.fixAfter significant AST manipulations (e.g., deleting a node, moving files), re-obtain the relevant nodes from the project or their parents. If a node is no longer needed, ensure you don't try to access its properties or methods.
Warnings
- breaking Major versions of ts-morph are typically released to support new TypeScript compiler versions. Upgrading ts-morph often requires upgrading your project's TypeScript version to match its peer dependency, and vice-versa.
- breaking Version 28.0.0 introduces support for TypeScript 6.0, which may include breaking changes aligning with the underlying TypeScript API changes.
- breaking The method `Node.prototype.forgetDescendants()` in ts-morph v22.0.0 and later no longer returns the node itself. It now returns `void`.
- gotcha Browser support for ts-morph was temporarily broken in version 26.0.0 due to missing browser fields in its `package.json`, causing issues with modules like `fs/promises` in browser environments.
Install
-
npm install ts-morph -
yarn add ts-morph -
pnpm add ts-morph
Imports
- Project
const Project = require('ts-morph').Project;import { Project } from 'ts-morph'; - StructureKind
import * as ts from 'ts-morph'; const StructureKind = ts.StructureKind;
import { StructureKind } from 'ts-morph'; - Node
import { SyntaxKind } from 'typescript'; // Not the ts-morph Node classimport { Node } from 'ts-morph';
Quickstart
import { Project, StructureKind } from "ts-morph";
async function main() {
const project = new Project({
// Optionally specify compiler options, tsconfig.json, or an in-memory file system.
// If initialized with a tsconfig.json, it automatically loads associated source files.
});
// Add source files or create new ones
project.addSourceFilesAtPaths("src/**/*.ts");
const myClassFile = project.createSourceFile("src/MyClass.ts", "export class MyClass {}");
const myEnumFile = project.createSourceFile("src/MyEnum.ts", {
statements: [{
kind: StructureKind.Enum,
name: "MyEnum",
isExported: true,
members: [{ name: "member" }],
}],
});
// Get and manipulate nodes
const myClass = myClassFile.getClassOrThrow("MyClass");
console.log(myClass.getName()); // "MyClass"
myClass.rename("NewName");
myClass.addProperty({
name: "myProp",
initializer: "5",
has;// Indicates if property should have a question mark (optional) or not
});
// Asynchronously save all changes to the file system
await project.save();
console.log('Project changes saved.');
}
main().catch(console.error);