CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ts-morph

TypeScript compiler wrapper for static analysis and code manipulation.

Pending
Overview
Eval results
Files

type-system.mddocs/

Type System Integration

Direct access to TypeScript's type checker and type information for advanced static analysis and type-aware transformations. This enables sophisticated analysis of TypeScript code beyond syntax-level operations.

Capabilities

Type Checker

The TypeScript type checker provides comprehensive type analysis capabilities.

class TypeChecker {
  /** Get the type of a node at its location */
  getTypeAtLocation(node: Node): Type;
  
  /** Get the symbol at a specific location */
  getSymbolAtLocation(node: Node): Symbol | undefined;
  
  /** Get the type of a symbol at a specific location */
  getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type;
  
  /** Get the apparent type (after applying type arguments) */
  getApparentType(type: Type): Type;
  
  /** Get the base type of a type */
  getBaseTypeOfLiteralType(type: Type): Type;
  
  /** Get the full type of a node (including contextual type) */
  getFullTypeAtLocation(node: Node): Type;
  
  /** Get the contextual type for an expression */
  getContextualType(node: Expression): Type | undefined;
  
  /** Get the signature from a declaration */
  getSignatureFromDeclaration(node: SignatureDeclaration): Signature | undefined;
  
  /** Get all signatures for a type */
  getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
  
  /** Get the return type of a signature */
  getReturnTypeOfSignature(signature: Signature): Type;
  
  /** Check if types are assignable */
  isTypeAssignableTo(source: Type, target: Type): boolean;
  
  /** Get exports of a module */
  getExportsOfModule(moduleSymbol: Symbol): Symbol[];
  
  /** Get properties of a type */
  getPropertiesOfType(type: Type): Symbol[];
  
  /** Get all accessible symbols at location */
  getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
}

enum SignatureKind {
  Call = 0,
  Construct = 1,
}

Type Class

Represents a TypeScript type with comprehensive type information and analysis methods.

class Type {
  /** Get the string representation of the type */
  getText(enclosingNode?: Node, typeFormatFlags?: TypeFormatFlags): string;
  
  /** Get the symbol associated with this type */
  getSymbol(): Symbol | undefined;
  
  /** Get the apparent type (after type argument substitution) */
  getApparentType(): Type;
  
  /** Get constraint of a type parameter */
  getConstraint(): Type | undefined;
  
  /** Get default type of a type parameter */
  getDefault(): Type | undefined;
  
  /** Get union types if this is a union type */
  getUnionTypes(): Type[];
  
  /** Get intersection types if this is an intersection type */
  getIntersectionTypes(): Type[];
  
  /** Get type arguments if this is a generic type instance */
  getTypeArguments(): Type[];
  
  /** Get call signatures */
  getCallSignatures(): Signature[];
  
  /** Get construct signatures */
  getConstructSignatures(): Signature[];
  
  /** Get array element type if this is an array type */
  getArrayElementType(): Type | undefined;
  
  /** Get number index type */
  getNumberIndexType(): Type | undefined;
  
  /** Get string index type */
  getStringIndexType(): Type | undefined;
  
  /** Get base types */
  getBaseTypes(): Type[];
  
  /** Check if this is an array type */
  isArray(): boolean;
  
  /** Check if this is a boolean type */
  isBoolean(): boolean;
  
  /** Check if this is a string type */
  isString(): boolean;
  
  /** Check if this is a number type */
  isNumber(): boolean;
  
  /** Check if this is a literal type */
  isLiteral(): boolean;
  
  /** Check if this is a boolean literal type */
  isBooleanLiteral(): boolean;
  
  /** Check if this is an enum literal type */
  isEnumLiteral(): boolean;
  
  /** Check if this is a number literal type */
  isNumberLiteral(): boolean;
  
  /** Check if this is a string literal type */
  isStringLiteral(): boolean;
  
  /** Check if this is a class type */
  isClass(): boolean;
  
  /** Check if this is a class or interface type */
  isClassOrInterface(): boolean;
  
  /** Check if this is an enum type */
  isEnum(): boolean;
  
  /** Check if this is an interface type */
  isInterface(): boolean;
  
  /** Check if this is an object type */
  isObject(): boolean;
  
  /** Check if this is a type parameter */
  isTypeParameter(): boolean;
  
  /** Check if this is a union type */
  isUnion(): boolean;
  
  /** Check if this is an intersection type */
  isIntersection(): boolean;
  
  /** Check if this is an anonymous type */
  isAnonymous(): boolean;
  
  /** Check if this is null */
  isNull(): boolean;
  
  /** Check if this is undefined */
  isUndefined(): boolean;
  
  /** Check if this is never */
  isNever(): boolean;
  
  /** Check if this is unknown */
  isUnknown(): boolean;
  
  /** Check if this is any */
  isAny(): boolean;
  
  /** Get literal value for literal types */
  getLiteralValue(): string | number | boolean | undefined;
  
  /** Get properties of this type */
  getProperties(): Symbol[];
  
  /** Get property by name */
  getProperty(name: string): Symbol | undefined;
  
  /** Get non-nullable type (removes null and undefined) */
  getNonNullableType(): Type;
}

Symbol Class

Represents a TypeScript symbol with name, declarations, and type information.

class Symbol {
  /** Get the symbol name */
  getName(): string;
  
  /** Get the escaped name */
  getEscapedName(): string;
  
  /** Get all declarations of this symbol */
  getDeclarations(): Node[];
  
  /** Get first declaration */
  getFirstDeclaration(): Node | undefined;
  
  /** Get value declaration (for symbols that have values) */
  getValueDeclaration(): Node | undefined;
  
  /** Get type declaration (for type symbols) */
  getTypeDeclaration(): Node | undefined;
  
  /** Get the symbol flags */
  getFlags(): SymbolFlags;
  
  /** Get exports if this is a module or namespace */
  getExports(): Symbol[];
  
  /** Get global exports */
  getGlobalExports(): Symbol[];
  
  /** Get members if this is a class or interface */
  getMembers(): Symbol[];
  
  /** Get the type of this symbol */
  getTypeAtLocation(location: Node): Type;
  
  /** Get documentation comment */
  getDocumentationComment(): SymbolDisplayPart[];
  
  /** Get JSDoc tags */
  getJsDocTags(): JSDocTagInfo[];
  
  /** Check if symbol has specific flag */
  hasFlag(flag: SymbolFlags): boolean;
  
  /** Check if symbol is optional */
  isOptional(): boolean;
}

Signature Class

Represents a function or method signature with parameter and return type information.

class Signature {
  /** Get parameters of this signature */
  getParameters(): Symbol[];
  
  /** Get return type */
  getReturnType(): Type;
  
  /** Get type parameters */
  getTypeParameters(): TypeParameter[];
  
  /** Get the declaration associated with this signature */
  getDeclaration(): SignatureDeclaration | undefined;
  
  /** Get documentation comment */
  getDocumentationComment(): SymbolDisplayPart[];
  
  /** Get JSDoc tags */
  getJsDocTags(): JSDocTagInfo[];
  
  /** Get signature string representation */
  getText(): string;
}

class TypeParameter extends Type {
  /** Get the constraint of this type parameter */
  getConstraint(): Type | undefined;
  
  /** Get the default type of this type parameter */
  getDefault(): Type | undefined;
}

Diagnostic Information

TypeScript diagnostic information for errors, warnings, and suggestions.

class Diagnostic {
  /** Get the diagnostic message text */
  getMessageText(): string;
  
  /** Get the source file where this diagnostic occurred */
  getSourceFile(): SourceFile | undefined;
  
  /** Get the start position */
  getStart(): number | undefined;
  
  /** Get the length */
  getLength(): number | undefined;
  
  /** Get the end position */
  getEnd(): number | undefined;
  
  /** Get diagnostic category */
  getCategory(): DiagnosticCategory;
  
  /** Get diagnostic code */
  getCode(): number;
  
  /** Get the line number (1-indexed) */
  getLineNumber(): number | undefined;
}

interface SymbolDisplayPart {
  text: string;
  kind: string;
}

interface JSDocTagInfo {
  name: string;
  text?: SymbolDisplayPart[];
}

Language Service

TypeScript language service providing IDE-like functionality.

class LanguageService {
  /** Get completions at a position */
  getCompletionsAtPosition(fileName: string, position: number): CompletionInfo | undefined;
  
  /** Get quick info at a position */
  getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined;
  
  /** Get definition at a position */
  getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
  
  /** Get type definition at a position */
  getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
  
  /** Get references at a position */
  getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
  
  /** Get semantic diagnostics */
  getSemanticDiagnostics(fileName: string): Diagnostic[];
  
  /** Get syntactic diagnostics */
  getSyntacticDiagnostics(fileName: string): Diagnostic[];
  
  /** Get suggestion diagnostics */
  getSuggestionDiagnostics(fileName: string): DiagnosticWithLocation[];
  
  /** Find all references */
  findReferences(fileName: string, position: number): ReferencedSymbol[];
  
  /** Get rename info */
  getRenameInfo(fileName: string, position: number): RenameInfo;
  
  /** Find rename locations */
  findRenameLocations(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameLocation[];
}

interface CompletionInfo {
  entries: CompletionEntry[];
  isGlobalCompletion: boolean;
  isMemberCompletion: boolean;
  isNewIdentifierLocation: boolean;
}

interface CompletionEntry {
  name: string;
  kind: string;
  kindModifiers?: string;
  sortText: string;
  insertText?: string;
  replacementSpan?: TextSpan;
}

interface QuickInfo {
  kind: string;
  kindModifiers: string;
  textSpan: TextSpan;
  displayParts?: SymbolDisplayPart[];
  documentation?: SymbolDisplayPart[];
  tags?: JSDocTagInfo[];
}

interface DefinitionInfo {
  fileName: string;
  textSpan: TextSpan;
  kind: string;
  name: string;
  containerKind: string;
  containerName: string;
}

interface TextSpan {
  start: number;
  length: number;
}

Usage Examples:

import { Project, SyntaxKind } from "ts-morph";

const project = new Project({
  tsConfigFilePath: "tsconfig.json",
});

const sourceFile = project.createSourceFile("analysis.ts", `
interface User {
  id: number;
  name: string;
  email?: string;
}

class UserService {
  private users: User[] = [];
  
  async getUser(id: number): Promise<User | undefined> {
    return this.users.find(user => user.id === id);
  }
}

const service = new UserService();
const user = service.getUser(1);
`);

const typeChecker = project.getTypeChecker();

// Analyze types
const userInterface = sourceFile.getInterfaceOrThrow("User");
const userType = typeChecker.getTypeAtLocation(userInterface);

console.log("User type text:", userType.getText());
console.log("Is User an object type:", userType.isObject());

// Get properties of User interface
const properties = userType.getProperties();
properties.forEach(prop => {
  const propType = typeChecker.getTypeOfSymbolAtLocation(prop, userInterface);
  console.log(\`Property \${prop.getName()}: \${propType.getText()}\`);
  console.log(\`Is optional: \${prop.isOptional()}\`);
});

// Analyze method return types
const userServiceClass = sourceFile.getClassOrThrow("UserService");
const getUserMethod = userServiceClass.getMethodOrThrow("getUser");
const signature = typeChecker.getSignatureFromDeclaration(getUserMethod);

if (signature) {
  const returnType = signature.getReturnType();
  console.log("getUser return type:", returnType.getText());
  console.log("Is Promise:", returnType.getText().startsWith("Promise"));
  
  // Get the actual type inside Promise
  const typeArgs = returnType.getTypeArguments();
  if (typeArgs.length > 0) {
    console.log("Promise type argument:", typeArgs[0].getText());
  }
}

// Analyze variable types
const serviceVariable = sourceFile.getVariableDeclarationOrThrow("service");
const serviceType = typeChecker.getTypeAtLocation(serviceVariable);
console.log("Service variable type:", serviceType.getText());

// Find all number literal types
const numberLiterals = sourceFile.getDescendantsOfKind(SyntaxKind.NumericLiteral);
numberLiterals.forEach(literal => {
  const type = typeChecker.getTypeAtLocation(literal);
  console.log(\`Number literal \${literal.getText()} has type: \${type.getText()}\`);
  console.log("Is number literal type:", type.isNumberLiteral());
  if (type.isNumberLiteral()) {
    console.log("Literal value:", type.getLiteralValue());
  }
});

// Check type assignability
const numberType = typeChecker.getTypeAtLocation(
  sourceFile.getFirstDescendantByKindOrThrow(SyntaxKind.NumericLiteral)
);
const stringType = typeChecker.getTypeAtLocation(
  sourceFile.addVariableStatement({
    declarationKind: "const",
    declarations: [{ name: "temp", initializer: "'hello'" }]
  }).getDeclarations()[0]
);

console.log("Can assign string to number:", 
  typeChecker.isTypeAssignableTo(stringType, numberType));

Advanced Type Analysis:

import { Project } from "ts-morph";

const project = new Project();
const sourceFile = project.createSourceFile("advanced.ts", `
type EventHandler<T> = (event: T) => void;

interface ClickEvent {
  type: 'click';
  x: number;
  y: number;
}

interface KeyEvent {
  type: 'key';
  key: string;
  code: number;
}

type AllEvents = ClickEvent | KeyEvent;

class EventEmitter {
  private handlers: Map<string, EventHandler<any>[]> = new Map();
  
  on<T extends AllEvents>(type: T['type'], handler: EventHandler<T>): void {
    // Implementation
  }
}
`);

const typeChecker = project.getTypeChecker();

// Analyze generic types
const eventHandlerType = sourceFile.getTypeAliasOrThrow("EventHandler");
const handlerType = typeChecker.getTypeAtLocation(eventHandlerType);

console.log("EventHandler type:", handlerType.getText());

// Analyze union types
const allEventsType = sourceFile.getTypeAliasOrThrow("AllEvents");
const unionType = typeChecker.getTypeAtLocation(allEventsType);

console.log("AllEvents is union:", unionType.isUnion());
if (unionType.isUnion()) {
  const unionTypes = unionType.getUnionTypes();
  unionTypes.forEach((type, index) => {
    console.log(\`Union member \${index}: \${type.getText()}\`);
    
    // Analyze each union member's properties
    const properties = type.getProperties();
    properties.forEach(prop => {
      const propType = typeChecker.getTypeOfSymbolAtLocation(prop, allEventsType);
      console.log(\`  Property \${prop.getName()}: \${propType.getText()}\`);
    });
  });
}

// Analyze generic method constraints
const eventEmitterClass = sourceFile.getClassOrThrow("EventEmitter");
const onMethod = eventEmitterClass.getMethodOrThrow("on");
const onSignature = typeChecker.getSignatureFromDeclaration(onMethod);

if (onSignature) {
  const typeParams = onSignature.getTypeParameters();
  typeParams.forEach(typeParam => {
    console.log("Type parameter:", typeParam.getText());
    const constraint = typeParam.getConstraint();
    if (constraint) {
      console.log("Constraint:", constraint.getText());
    }
  });
}

Install with Tessl CLI

npx tessl i tessl/npm-ts-morph

docs

ast-nodes.md

code-generation.md

file-system.md

index.md

project-management.md

type-system.md

tile.json