Skip to main content
doc0blade47/fumadocsdocumentation home
Sign in

TypeScript Type Table Extraction

Last updated Oct 12, 2026
Relevant source files

The following files were used as context for generating this wiki page:

Overview

TypeScript type table extraction bridges raw source code types and rendered documentation interfaces by automatically inspecting TypeScript files, parsing JSDoc annotations, and transforming property definitions into interactive API tables packages/typescript/src/lib/base.ts:1-164, packages/typescript/src/ui/auto-type-table.tsx. Powered by ts-morph and unified remark plugins, the system extracts symbol metadata, processes tags like @default and @param, and serializes structured property entries for compile-time MDX replacement and runtime client rendering packages/typescript/src/lib/base.ts:167-230, packages/typescript/src/lib/remark-auto-type-table.ts.

Sources: packages/typescript/src/lib/base.ts:1-164, packages/typescript/src/lib/base.ts:167-230, packages/typescript/src/lib/remark-auto-type-table.ts:49-108, packages/typescript/src/ui/auto-type-table.tsx:1-80

Type Table Generator Architecture

Generator Factory and Configuration

The type table generator exposes public APIs for initializing type inspection environments and processing source declarations. The createGenerator function accepts GeneratorOptions, configuring optional project instances and cache mechanisms. If no custom Project instance is provided via options.project, createProject instantiates a new ts-morph Project pointing to tsconfigPath (defaulting to ./tsconfig.json) with skipAddingFilesFromTsConfig set to true.

export async function createProject(options: TypescriptConfig = {}): Promise<Project> {
  const { Project } = await import('ts-morph');
  return new Project({
    tsConfigFilePath: options.tsconfigPath ?? './tsconfig.json',
    skipAddingFilesFromTsConfig: true,
  });
}
 
export function createGenerator(options: GeneratorOptions = {}) {
  const cache = options?.cache ? options.cache : null;
  let instance: Project | Promise<Project> | undefined = options?.project;
 
  function getProject() {
    if (instance) return instance;
    return (instance = createProject(options));
  }

Sources: packages/typescript/src/lib/base.ts:86-101

Option PropertyTypeDefault ValuePurpose
tsconfigPathstring'./tsconfig.json'Path to the TypeScript configuration file.
cacheCache | falsefalseEnables caching for generated documentation results.
projectProjectundefinedPre-instantiated ts-morph project instance.
allowInternalbooleanfalseAllows fields tagged with @internal.
transformTransformerundefinedCustom function to modify property documentation entries.
typeSimplifierTypeSimplifierOptionsundefinedConfiguration options for type simplification.

Sources: packages/typescript/src/lib/base.ts:53-84

Note

When options.cache is active, documentation output is hashed using generateHash incorporating the file path, export name, file content, and package version before consulting the cache store.

Sources: packages/typescript/src/lib/base.ts:94-95, packages/typescript/src/lib/base.ts:129-134

Type Table Properties and Output Resolution

The BaseTypeTableProps and GenerateTypeTableOptions interfaces govern how individual type tables are located and generated from file paths or inline type definitions. The getTypeTableOutput helper resolves relative file paths against an optional basePath, reads source files asynchronously, and handles inline type strings.

export interface BaseTypeTableProps {
  /**
   * The path to source TypeScript file.
   */
  path?: string;
 
  /**
   * Exported type name to generate from.
   */
  name?: string;
 
  /**
   * Set the type to generate from.
   *
   * When used with `name`, it generates the type with `name` as export name.
   *
   * ```ts
   * export const myName = MyType;
   * ```
   *
   * When `type` contains multiple lines, `export const` is not added.
   * You need to export it manually, and specify the type name with `name`.
   *
   * ```tsx
   * <AutoTypeTable
   *   path="./file.ts"
   *   type={`import { ReactNode } from "react"
   *   export const MyName = ReactNode`}
   *   name="MyName"
   * />
   * ```
   */
  type?: string;
}
 
export interface GenerateTypeTableOptions extends GenerateOptions {
  /**
   * base path to resolve `path` prop
   */
  basePath?: string;
}

Sources: packages/typescript/src/lib/type-table.ts:5-45

When type contains multiple lines, it is appended directly to the file content without modification. If type is a single line, it is wrapped in an exported type declaration using the specified name or falling back to $Fumadocs.

if (type && type.split('\n').length > 1) {
  content += `\n${type}`;
} else if (type) {
  typeName ??= '$Fumadocs';
  content += `\nexport type ${typeName} = ${type}`;
}

Sources: packages/typescript/src/lib/type-table.ts:61-66

Warning

If a requested export name is provided but yields an empty documentation output array, getTypeTableOutput throws an error stating that the export does not exist in the target file.

Sources: packages/typescript/src/lib/type-table.ts:74-75

Abstract Syntax Tree Extraction Pipeline

AST Processing and Call-Chain Execution

The AST extraction pipeline processes documentation requests through a strict sequence of calls initiated by remark plugins and handled by generator modules. The execution trace flows through remarkAutoTypeTable to createGenerator, then invokes generate before iterating over individual properties via getDocEntry.

Sources: packages/typescript/src/lib/remark-auto-type-table.ts:183-186, packages/typescript/src/lib/base.ts:153-153, packages/typescript/src/lib/base.ts:180-180

  1. remarkAutoTypeTable triggers generator.generateTypeTable with configuration options and resolved paths. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:183-186
  2. createGenerator resolves the source file within the ts-morph project instance and iterates over exported declarations matching the target name. Sources: packages/typescript/src/lib/base.ts:136-141
  3. generate constructs an EntryContext, extracts top-level documentation comments, and retrieves all type properties. Sources: packages/typescript/src/lib/base.ts:167-182
  4. getDocEntry inspects each property symbol, evaluating JSDoc tags, optional status, and formatting types via ts-morph and type simplifiers. Sources: packages/typescript/src/lib/base.ts:192-229

Warning

If a class declaration property name starts with a hash character (#), getDocEntry immediately returns undefined to filter out private class fields.

Sources: packages/typescript/src/lib/base.ts:196-198

Symbol Analysis and Declaration Handling

The generate function extracts top-level documentation comments from compiler symbols using getDocumentationComment with the program's type checker compiler object, converting display parts into strings via ts.displayPartsToString.

const comment = declaration
  .getSymbol()
  ?.compilerSymbol.getDocumentationComment(program.getTypeChecker().compilerObject);

Sources: packages/typescript/src/lib/base.ts:175-177

For each property symbol discovered on the declaration type, getDocEntry evaluates tags and formats types using ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope combined with ts.TypeFormatFlags.NoTruncation.

type: subType.getText(
  context.declaration,
  ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope | ts.TypeFormatFlags.NoTruncation,
),

Sources: packages/typescript/src/lib/base.ts:219-222

Important

When allowInternal is disabled (default false), any property containing an @internal JSDoc tag causes getDocEntry to abort and omit the entry entirely.

Sources: packages/typescript/src/lib/base.ts:204-205

Tag Parsing and Metadata Extraction

Call-Chain Execution Walkthrough

The extraction of metadata from JSDoc tags follows a precise sequence starting from the remark plugin AST traversal down to individual tag parsing.

  1. remarkAutoTypeTable initializes the transformation and invokes generate on the generator instance. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:183-186
  2. generate processes document entries and passes them to buildTypeProp for property-level AST construction. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:49-52
  3. buildTypeProp executes onItem for each individual DocEntry record. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:53-53
  4. onItem calls parseTags passing the raw tag array attached to the entry. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:55-55
  5. parseTags iterates over the raw tags, mapping recognized field names into structured TypedTags objects. Sources: packages/typescript/src/lib/parse-tags.ts:17-44

Sources: packages/typescript/src/lib/remark-auto-type-table.ts:49-55, packages/typescript/src/lib/remark-auto-type-table.ts:183-186, packages/typescript/src/lib/parse-tags.ts:17-17

Tag Interpretation and Field Mapping

The parseTags function inspects each RawTag item, evaluating its name key to extract configuration values, parameter lists, and return descriptions. When the tag name matches default or defaultValue, the text content is assigned directly to typed.default.

for (const { name: key, text } of tags) {
  if (key === 'default' || key === 'defaultValue') {
    typed.default = text;
    continue;
  }

Sources: packages/typescript/src/lib/parse-tags.ts:20-24

For param tags, text parsing splits strings around the first hyphen (-) separator to isolate the parameter name from its descriptive text. If no hyphen is present, the entire trimmed text serves as the parameter name with an empty description.

if (key === 'param') {
  const sepIdx = text.indexOf('-');
  const param = sepIdx === -1 ? text.trim() : text.slice(0, sepIdx).trim();
  const description = sepIdx === -1 ? '' : text.slice(sepIdx + 1).trim();
 
  typed.params ??= [];
  typed.params.push({
    name: param,
    description,
  });
  continue;
}

Sources: packages/typescript/src/lib/parse-tags.ts:26-37

Tag Name KeyMapped TypedTags PropertyInterpretation Logic
default, defaultValuedefaultAssigns raw text value directly to the default property.
paramparamsSplits text at the first - character into parameter name and description.
returnsreturnsAssigns return documentation text directly to the returns property.

Sources: packages/typescript/src/lib/parse-tags.ts:20-41

Note

Unrecognized tag names encountered during the iteration loop in parseTags are silently ignored without throwing errors or populating structured properties.

Sources: packages/typescript/src/lib/parse-tags.ts:20-43

Markdown Transformation and AST Replacement

Remark Plugin Traversal and Execution Flow

The remarkAutoTypeTable plugin operates as a unified transformer, walking the markdown Abstract Syntax Tree to locate automatic type table directives and replace them with interactive MDX flow elements. The plugin initializes a markdown renderer using either custom render options or the default markdownRenderer configured with Shiki syntax highlighting.

export function remarkAutoTypeTable(
  config: RemarkAutoTypeTableOptions = {},
): Transformer<Root, Root> {
  const {
    name = 'auto-type-table',
    outputName = 'TypeTable',
    options: generateOptions = {},
    remarkStringify = true,
    generator = createGenerator(),
    renderMarkdown,
    renderType,
    shiki,
  } = config;
  let renderer: MarkdownRenderer;
 
  if (renderMarkdown && renderType) {
    renderer = { renderMarkdownToHast: renderMarkdown, renderTypeToHast: renderType };
  } else {
    renderer = markdownRenderer(shiki);
    if (renderMarkdown) renderer.renderMarkdownToHast = renderMarkdown;
    if (renderType) renderer.renderTypeToHast = renderType;
  }
 

Sources: packages/typescript/src/lib/remark-auto-type-table.ts:150-172

During AST traversal, the execution walkthrough follows a strict call chain from the remark transformer down to object expression builders:

  1. remarkAutoTypeTable processes document entries and invokes generate. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:173-186
  2. generate calls buildTypeProp passing document entries and the markdown renderer. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:49-52
  3. buildTypeProp executes onItem for each individual DocEntry record. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:53-53
  4. onItem invokes objectBuilder to construct property-level expression objects. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:54-54
  5. objectBuilder accumulates property nodes and expression statements for estree serialization. Sources: packages/typescript/src/lib/remark-auto-type-table.ts:15-35

Sources: packages/typescript/src/lib/remark-auto-type-table.ts:49-54, packages/typescript/src/lib/remark-auto-type-table.ts:173-186, packages/typescript/src/lib/remark-auto-type-table.ts:15-20

Prop Synthesis and AST Node Substitution

The buildTypeProp function constructs an Estree ObjectExpression mapping property names to their serialized metadata. Each entry renders its simplified type, full type description, required flag, default value, return description, parameters, and deprecation status into hast nodes before converting them into estree expressions.

async function buildTypeProp(
  entries: DocEntry[],
  renderer: MarkdownRenderer,
): Promise<ObjectExpression> {
  async function onItem(entry: DocEntry) {
    const node = objectBuilder();
    const tags = parseTags(entry.tags);
    node.addJsxProperty('type', await renderer.renderTypeToHast(entry.simplifiedType));
    node.addJsxProperty('typeDescription', await renderer.renderTypeToHast(entry.type));
    node.addExpressionNode('required', valueToEstree(entry.required));
 
    if (entry.typeHref)
      node.addExpressionNode('typeDescriptionLink', valueToEstree(entry.typeHref));
 
    if (tags.default) node.addJsxProperty('default', await renderer.renderTypeToHast(tags.default));
 
    if (tags.returns)
      node.addJsxProperty('returns', await renderer.renderMarkdownToHast(tags.returns));
 
    if (tags.params) {
      node.addExpressionNode('parameters', {
        type: 'ArrayExpression',
        elements: await Promise.all(tags.params.map(onParam)),
      });
    }
 
    if (entry.description) {
      node.addJsxProperty('description', await renderer.renderMarkdownToHast(entry.description));
    }
 
    if (entry.deprecated) {
      node.addExpressionNode('deprecated', valueToEstree(true));
    }
 
    return node.build();
  }

Sources: packages/typescript/src/lib/remark-auto-type-table.ts:49-84

The generated output forms an MdxJsxFlowElement node carrying attributes for the table ID, stringified JSON fallback, and embedded estree program body.

const rendered: MdxJsxFlowElement[] = [];
 
for (const doc of output) {
  rendered.push({
    type: 'mdxJsxFlowElement',
    name: outputName,
    attributes: [
      {
        type: 'mdxJsxAttribute',
        name: 'id',
        value: `type-table-${doc.id}`,
      },
      {
        type: 'mdxJsxAttribute',
        name: 'type',
        value: {
          type: 'mdxJsxAttributeValueExpression',
          value: remarkStringify ? JSON.stringify(doc, null, 2) : '',
          data: {
            estree: {
              type: 'Program',
              sourceType: 'module',
              body: [
                {
                  type: 'ExpressionStatement',
                  expression: await buildTypeProp(doc.entries, renderer),
                },
              ],
            },
          },
        },
      },
      ...attributes,
    ],
    children: [],
  });
}

Sources: packages/typescript/src/lib/remark-auto-type-table.ts:187-223

Warning

When remarkStringify is enabled in configuration options, JSON.stringify populates the attribute value string alongside the compiled estree program expression to support legacy stringifiers.

Sources: packages/typescript/src/lib/remark-auto-type-table.ts:157-157, packages/typescript/src/lib/remark-auto-type-table.ts:202-205

Interactive Client Table Rendering

Runtime Component and Hast Conversion

The AutoTypeTable component provides server-side or runtime rendering for interactive type tables by invoking the specified generator and transforming parsed doc entries into UI nodes. When renderType and renderMarkdown functions are omitted from AutoTypeTableProps, a default Markdown renderer initialized with Shiki options converts type signatures and docstrings into hast trees, which toJsx then transforms into React elements using toJsxRuntime.

export async function AutoTypeTable({
  generator,
  options,
  renderType,
  renderMarkdown,
  shiki,
  name,
  path,
  type,
  ...props
}: AutoTypeTableProps) {
  if (!renderType || !renderMarkdown) {
    const renderer = markdownRenderer(shiki);
    renderType ??= async (v) => toJsx(await renderer.renderTypeToHast(v));
    renderMarkdown ??= async (v) => toJsx(await renderer.renderMarkdownToHast(v));
  }

Sources: packages/typescript/src/ui/auto-type-table.tsx:23-38

The helper function toJsx wraps toJsxRuntime with the React JSX runtime and default MDX components while explicitly setting the img component override to undefined.

function toJsx(hast: Nodes) {
  return toJsxRuntime(hast, {
    ...JsxRuntime,
    components: { ...defaultMdxComponents, img: undefined },
  });
}

Sources: packages/typescript/src/ui/auto-type-table.tsx:81-86

Note

The AutoTypeTable component exports from packages/typescript/src/ui/index.ts, making it available for direct inclusion in client and server component contexts.

Sources: packages/typescript/src/ui/index.ts:1-1

Table Display and Collapsible Row Management

The TypeTable component from fumadocs-ui renders a container holding individual property rows using the Item subcomponent, utilizing translation notes keyed by type table. Each entry maps property keys to TypeNode definitions containing type elements, descriptions, defaults, required flags, and parameter lists.

export function TypeTable({
  id,
  type,
  className,
  ...props
}: { type: Record<string, TypeNode> } & ComponentProps<'div'>) {
  const t = useTranslations({ note: 'type table' });
 
  return (
    <div
      id={id}
      className={cn(
        '@container flex flex-col p-1 bg-fd-card text-fd-card-foreground rounded-2xl border my-6 text-sm overflow-hidden',
        className,
      )}
      {...props}
    >
      <div className="flex font-medium items-center px-3 py-1 not-prose text-fd-muted-foreground">
        <p className="w-1/4">{t('Prop')}</p>
        <p className="@max-xl:hidden">{t('Type')}</p>
      </div>
      {Object.entries(type).map(([key, value]) => (
        <Item key={key} parentId={id} name={key} item={value} />
      ))}
    </div>
  );
}

Sources: packages/radix-ui/src/components/type-table.tsx:52-78

PropertyType / InterfaceDefault / Meaning
descriptionReactNodeAdditional description of the field.
typeReactNodeShort type signature displayed in the main row view.
typeDescriptionReactNodeFull type signature displayed in the collapsible drawer.
typeDescriptionLinkstringOptional hyperlink destination for the type.
defaultReactNodeDefault value expression or tag rendering.
requiredbooleanIndicates if the property is mandatory (false appends ?).
deprecatedbooleanApplies line-through and muted styling when active.
parametersParameterNode[]List of parameter names and descriptions for function types.
returnsReactNodeReturn value description for function types.

Sources: packages/radix-ui/src/components/type-table.tsx:11-48

The Item component wraps each table row inside a Collapsible primitive, synchronizing open state changes with browser history hash updates so that deep links matching #${parentId}-${name} automatically expand the targeted property row upon load.

function Item({
  parentId,
  name,
  item: {
    parameters = [],
    description,
    required = false,
    deprecated,
    typeDescription,
    default: defaultValue,
    type,
    typeDescriptionLink,
    returns,
  },
}: {
  parentId?: string;
  name: string;
  item: TypeNode;
}) {
  const t = useTranslations({ note: 'type table' });
  const [open, setOpen] = useState(false);
  const id = parentId ? `${parentId}-${name}` : undefined;
 
  useEffect(() => {
    const hash = window.location.hash;
    if (!id || !hash) return;
    if (`#${id}` === hash) setOpen(true);
  }, [id]);
 
  return (
    <Collapsible
      id={id}
      open={open}
      onOpenChange={(v) => {
        if (v && id) {
          window.history.replaceState(null, '', `#${id}`);
        }
        setOpen(v);
      }}
      className={cn(
        'rounded-xl border overflow-hidden scroll-m-20 transition-all',
        open ? 'shadow-sm bg-fd-background not-last:mb-2' : 'border-transparent',
      )}
    >

Sources: packages/radix-ui/src/components/type-table.tsx:80-123

Warning

If a window location hash matches a property row identifier on mount, the useEffect hook forces open to true, overriding default closed states for direct URL navigation.

Sources: packages/radix-ui/src/components/type-table.tsx:103-107

Was this page helpful?