Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tree-sitter/tree-sitter/llms.txt

Use this file to discover all available pages before exploring further.

Introduction

In languages with static typing, it can be helpful for syntax trees to provide specific type information about individual syntax nodes. Tree-sitter makes this information available via a generated file called node-types.json. This node types file provides structured data about every possible syntax node in a grammar. You can use this data to generate type declarations in statically-typed programming languages.

File Location

The node-types.json file is generated when you build a grammar and is typically located in the src/ directory of a parser:
tree-sitter-javascript/
├── grammar.js
├── src/
   ├── parser.c
   ├── node-types.json Generated file
   └── ...
└── ...

File Structure

The node types file contains an array of objects, each describing a particular type of syntax node.

Basic Information

Every object in the array has these two entries:
type
string
required
Indicates which grammar rule the node represents. This corresponds to the ts_node_type function.
named
boolean
required
Indicates whether this node corresponds to a rule name in the grammar (true) or just a string literal (false).

Examples

Named Node
{
  "type": "string_literal",
  "named": true
}
Anonymous Node
{
  "type": "+",
  "named": false
}
Together, the type and named fields constitute a unique identifier for a node type. No two top-level objects in node-types.json should have the same values for both fields.

Internal Nodes

Many syntax nodes can have children. The node type object describes possible children using these entries:
fields
object
An object describing the possible fields that the node can have. Keys are field names, and values are child type objects.
children
object
A child type object describing all the node’s possible named children without fields.

Child Type Objects

A child type object describes a set of child nodes:
required
boolean
Whether there is always at least one node in this set
multiple
boolean
Whether there can be multiple nodes in this set
types
array
An array of objects representing the possible types of nodes in this set. Each object has type and named keys.

Example with Fields

{
  "type": "method_definition",
  "named": true,
  "fields": {
    "body": {
      "multiple": false,
      "required": true,
      "types": [
        { "type": "statement_block", "named": true }
      ]
    },
    "decorator": {
      "multiple": true,
      "required": false,
      "types": [
        { "type": "decorator", "named": true }
      ]
    },
    "name": {
      "multiple": false,
      "required": true,
      "types": [
        { "type": "computed_property_name", "named": true },
        { "type": "property_identifier", "named": true }
      ]
    },
    "parameters": {
      "multiple": false,
      "required": true,
      "types": [
        { "type": "formal_parameters", "named": true }
      ]
    }
  }
}
This describes a method_definition node that:
  • Has a required body field (always a statement_block)
  • May have zero or more decorator fields
  • Has a required name field (either computed_property_name or property_identifier)
  • Has a required parameters field (always formal_parameters)

Example with Children

{
  "type": "array",
  "named": true,
  "fields": {},
  "children": {
    "multiple": true,
    "required": false,
    "types": [
      { "type": "_expression", "named": true },
      { "type": "spread_element", "named": true }
    ]
  }
}
This describes an array node that can contain zero or more children, where each child is either an _expression or a spread_element.

Supertype Nodes

In Tree-sitter grammars, certain rules represent abstract categories of syntax nodes (e.g., “expression”, “type”, “declaration”). In the grammar.js file, these are often written as hidden rules whose definition is a simple choice where each member is just a single symbol. Normally, hidden rules don’t appear in the node types file. But if you add a hidden rule to the grammar’s supertypes list, then it will show up in the node types file with a special entry:
subtypes
array
An array of objects that specify the types of nodes this ‘supertype’ node can wrap.

Example

{
  "type": "_declaration",
  "named": true,
  "subtypes": [
    { "type": "class_declaration", "named": true },
    { "type": "function_declaration", "named": true },
    { "type": "generator_function_declaration", "named": true },
    { "type": "lexical_declaration", "named": true },
    { "type": "variable_declaration", "named": true }
  ]
}
Supertype nodes also appear elsewhere in the node types file as children of other node types:
{
  "type": "export_statement",
  "named": true,
  "fields": {
    "declaration": {
      "multiple": false,
      "required": false,
      "types": [
        { "type": "_declaration", "named": true }
      ]
    },
    "source": {
      "multiple": false,
      "required": false,
      "types": [
        { "type": "string", "named": true }
      ]
    }
  }
}
Using supertypes makes the node types file much shorter and easier to read, because a single supertype takes the place of multiple subtypes.

Using Node Types for Code Generation

The node-types.json file can be used to generate type-safe bindings for your language. Here’s an example of how you might process it:
import json

# Load node types
with open('node-types.json') as f:
    node_types = json.load(f)

# Generate TypeScript types
print('export type NodeType =')
for i, node_type in enumerate(node_types):
    if node_type['named']:
        separator = '|' if i < len(node_types) - 1 else ';'
        print(f"  | '{node_type['type']}' {separator}")

Real-World Example

Here’s how the TypeScript parser for tree-sitter-typescript uses node types to generate TypeScript definitions:
// Generated from node-types.json
export type SyntaxNode =
  | ProgramNode
  | FunctionDeclarationNode
  | ClassDeclarationNode
  | VariableDeclarationNode
  // ... more node types

export interface FunctionDeclarationNode extends NamedNodeBase {
  type: 'function_declaration';
  nameNode: IdentifierNode;
  parametersNode: FormalParametersNode;
  bodyNode: StatementBlockNode;
}

export interface IdentifierNode extends NamedNodeBase {
  type: 'identifier';
  text: string;
}

// ... more interfaces

Benefits of Using Node Types

Type Safety

Generate type-safe code that prevents runtime errors when working with syntax trees.

IDE Support

Enable autocomplete and inline documentation in your IDE.

Refactoring

Safely refactor code that works with syntax trees.

Documentation

Auto-generate documentation about your grammar’s structure.

Accessing Node Types at Runtime

You can also access node type information programmatically:
const TSLanguage *language = tree_sitter_javascript();

// Get the number of node types
uint32_t symbol_count = ts_language_symbol_count(language);

// Get the name of a specific node type
for (uint32_t i = 0; i < symbol_count; i++) {
  const char *name = ts_language_symbol_name(language, i);
  TSSymbolType type = ts_language_symbol_type(language, i);
  
  if (type == TSSymbolTypeRegular) {
    printf("Named node: %s\n", name);
  } else if (type == TSSymbolTypeAnonymous) {
    printf("Anonymous node: %s\n", name);
  }
}

// Get field information
uint32_t field_count = ts_language_field_count(language);
for (uint32_t i = 1; i <= field_count; i++) {
  const char *field_name = ts_language_field_name_for_id(language, i);
  printf("Field: %s\n", field_name);
}

Next Steps

ABI Versions

Learn about parser compatibility and ABI versioning

Creating Parsers

Build your own Tree-sitter parser