Typescript
Classes
TypeScriptScanner
class TypeScriptScanner implements Scanner
Use TypeScriptScanner to extract API signatures, parameters, and return types from TypeScript and JavaScript source files so skrypt can generate documentation for them.
This is the scanner skrypt uses internally when it encounters .ts, .tsx, .js, .jsx, .mjs, or .cjs files during a skrypt generate run. You'd reach for it directly when building a custom pipeline — for example, scanning files programmatically before passing results to your own doc renderer, or integrating skrypt's extraction logic into a CI step.
TypeScriptScanner implements the Scanner interface, meaning it fits into the same plugin slot as scanners for other languages. Call canHandle() first to confirm the file is in scope, then call scanFile() to get the structured API data back. Declaration files (.d.ts) are intentionally skipped — the scanner targets source, not compiled output.
Parameters
canHandle(filePath)
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | Yes | Absolute or relative path to the file you want to check. Returns false for .d.ts files even if the extension otherwise matches. |
scanFile(filePath)
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | Yes | Path to the TypeScript or JavaScript source file to parse. Must be a file canHandle() already confirmed — passing an unsupported file type produces undefined behavior. |
Returns
canHandle() returns a boolean — use it as a guard before calling scanFile().
scanFile() returns a Promise<ScanResult> containing the extracted APIElement[] array for that file. Each element carries the name, signature, parameters, return type, and source language. Pass these elements to skrypt's doc generator or your own template renderer to produce documentation.
Heads up
languagesis a public property set to['typescript', 'javascript']— you can read it to discover what a scanner instance supports without callingcanHandle()on a specific path.scanFile()reads the file from disk at call time, so make sure the file exists and is readable before invoking it in async pipelines.
Example:
const path = require('path')
const fs = require('fs')
const os = require('os')
const ts = require('typescript')
// Inline types matching the Scanner interface
/**
* @typedef {{ name: string, signature: string, language: string, parameters: Array<{name: string, type: string}>, returnType: string }} APIElement
* @typedef {{ elements: APIElement[], filePath: string }} ScanResult
*/
// Minimal self-contained implementation mirroring TypeScriptScanner behavior
class TypeScriptScanner {
constructor() {
this.languages = ['typescript', 'javascript']
}
canHandle(filePath) {
return /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filePath) && !filePath.includes('.d.ts')
}
async scanFile(filePath) {
const source = fs.readFileSync(filePath, 'utf-8')
const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true)
const language = filePath.endsWith('.ts') || filePath.endsWith('.tsx') ? 'typescript' : 'javascript'
const elements = []
ts.forEachChild(sourceFile, (node) => {
if (ts.isFunctionDeclaration(node) && node.name) {
const params = node.parameters.map((p) => ({
name: p.name.getText(sourceFile),
type: p.type ? p.type.getText(sourceFile) : 'any',
}))
const returnType = node.type ? node.type.getText(sourceFile) : 'void'
elements.push({
name: node.name.getText(sourceFile),
signature: source.slice(node.pos, node.end).trim().split('{')[0].trim(),
language,
parameters: params,
returnType,
})
}
})
return { filePath, elements }
}
}
// Write a temporary TypeScript file to scan
const tmpFile = path.join(os.tmpdir(), 'example-api.ts')
fs.writeFileSync(tmpFile, `
export function createUser(name: string, email: string, role: 'admin' | 'member'): Promise<{ id: string }> {
return fetch('/users', { method: 'POST', body: JSON.stringify({ name, email, role }) })
.then(r => r.json())
}
export function deleteUser(userId: string): Promise<void> {
return fetch(\`/users/\${userId}\`, { method: 'DELETE' }).then(() => {})
}
`)
async function main() {
const scanner = new TypeScriptScanner()
if (!scanner.canHandle(tmpFile)) {
console.error('File type not supported')
process.exit(1)
}
try {
const result = await scanner.scanFile(tmpFile)
console.log(`Scanned: ${result.filePath}`)
console.log(`Found ${result.elements.length} API elements:\n`)
for (const el of result.elements) {
console.log(` ${el.name}`)
console.log(` Signature : ${el.signature}`)
console.log(` Parameters: ${el.parameters.map(p => `${p.name}: ${p.type}`).join(', ')}`)
console.log(` Returns : ${el.returnType}`)
console.log()
}
} catch (err) {
console.error('Scan failed:', err.message)
} finally {
fs.unlinkSync(tmpFile)
}
}
main()
// Expected output:
// Scanned: /tmp/example-api.ts
// Found 2 API elements:
//
// createUser
// Signature : export function createUser(name: string, email: string, role: 'admin' | 'member'): Promise<{ id: string }>
// Parameters: name: string, email: string, role: 'admin' | 'member'
// Returns : Promise<{ id: string }>
//
// deleteUser
// Signature : export function deleteUser(userId: string): Promise<void>
// Parameters: userId: string
// Returns : Promise<void>
Methods
canHandle
canHandle(filePath: string): boolean
Use canHandle to determine whether a TypeScriptScanner instance is capable of processing a given file before attempting to scan it.
Call this when routing files to the correct scanner — for example, when iterating over a mixed-language codebase and dispatching each file to the appropriate scanner. It's the gating check that prevents the TypeScript scanner from being invoked on Python or Go files.
Returns true for .ts, .tsx, .js, .jsx, .mjs, and .cjs files. TypeScript declaration files (.d.ts) are explicitly excluded, since they contain no implementation to document.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | Yes | Absolute or relative path to the file being evaluated — the extension and path determine the result. |
Returns
A boolean — true if the scanner can process the file, false otherwise. Use this as a guard before calling scanFile; passing an unsupported file directly to scanFile will produce unexpected results.
Heads up
.d.tsfiles returnfalseeven though they end in.ts. If you're seeing a TypeScript file skipped unexpectedly, check whether it's a declaration file.- The check is purely path-based — the file doesn't need to exist on disk for
canHandleto return a result.
Example:
// Inline types — no import from autodocs
interface Scanner {
languages: string[];
canHandle(filePath: string): boolean;
}
class TypeScriptScanner implements Scanner {
languages = ["typescript", "javascript"];
canHandle(filePath: string): boolean {
return (
/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filePath) &&
!filePath.includes(".d.ts")
);
}
}
const scanner = new TypeScriptScanner();
const files = [
"/project/src/api/users.ts",
"/project/src/components/Button.tsx",
"/project/src/utils/helpers.js",
"/project/src/types/index.d.ts",
"/project/src/server.py",
"/project/src/main.go",
"/project/dist/index.mjs",
];
try {
const results = files.map((filePath) => ({
filePath,
supported: scanner.canHandle(filePath),
}));
const supported = results.filter((r) => r.supported).map((r) => r.filePath);
const skipped = results.filter((r) => !r.supported).map((r) => r.filePath);
console.log("Will scan:", supported);
// Will scan: [
// '/project/src/api/users.ts',
// '/project/src/components/Button.tsx',
// '/project/src/utils/helpers.js',
// '/project/dist/index.mjs'
// ]
console.log("Skipped:", skipped);
// Skipped: [
// '/project/src/types/index.d.ts', ← declaration file excluded
// '/project/src/server.py',
// '/project/src/main.go'
// ]
} catch (err) {
console.error("Routing error:", err);
}
scanFile
async scanFile(filePath: string): Promise<ScanResult>
Use scanFile to extract all exported API elements from a TypeScript or JavaScript source file, returning structured metadata ready for documentation generation.
Reach for this when you're building a custom documentation pipeline and need to programmatically scan individual files rather than running skrypt generate across an entire directory. It's the core step in TypeScriptScanner's workflow — call it per file, then pass the results to your doc generator.
scanFile reads the file at the given path, parses it with the TypeScript compiler, and walks the AST to collect exported functions, classes, types, and their signatures. It automatically detects whether to treat the file as TypeScript or JavaScript based on the file extension.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | Yes | Absolute or relative path to the .ts, .tsx, .js, or .jsx file to scan. Declaration files (.d.ts) are not supported and will cause canHandle to return false. |
Returns
Returns a Promise<ScanResult> containing the detected language ("typescript" or "javascript") and an array of APIElement objects — one per exported symbol. Each APIElement includes the name, signature, parameters, return type, and source location. Pass the elements array directly to your AI doc generator or merge results across multiple scanFile calls before writing MDX output.
Heads up
- Only exported symbols are included. Internal functions and unexported types are silently skipped — if an element is missing from results, check that it has an
exportkeyword. scanFiledoes not follow imports or resolve cross-file types. Parameter types that reference other modules will appear as their raw string representation.
Example:
import { readFileSync, writeFileSync } from "fs";
import { resolve } from "path";
// Inline types matching ScanResult shape (do not import from autodocs)
interface Parameter {
name: string;
type: string;
required: boolean;
}
interface APIElement {
name: string;
kind: "function" | "class" | "type" | "interface";
signature: string;
parameters: Parameter[];
returnType: string;
filePath: string;
line: number;
}
interface ScanResult {
language: "typescript" | "javascript";
elements: APIElement[];
}
// Minimal mock of TypeScriptScanner for demonstration
class TypeScriptScanner {
canHandle(filePath: string): boolean {
return /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filePath) && !filePath.includes(".d.ts");
}
async scanFile(filePath: string): Promise<ScanResult> {
const language =
filePath.endsWith(".ts") || filePath.endsWith(".tsx")
? "typescript"
: "javascript";
// Mock AST walk — in production this uses the TypeScript compiler API
const mockElements: APIElement[] = [
{
name: "createUser",
kind: "function",
signature: "async createUser(email: string, role: UserRole): Promise<User>",
parameters: [
{ name: "email", type: "string", required: true },
{ name: "role", type: "UserRole", required: true },
],
returnType: "Promise<User>",
filePath,
line: 12,
},
{
name: "UserRole",
kind: "type",
signature: 'type UserRole = "admin" | "member" | "viewer"',
parameters: [],
returnType: "never",
filePath,
line: 4,
},
];
return { language, elements: mockElements };
}
}
async function main() {
const scanner = new TypeScriptScanner();
const targetFile = resolve("./src/users.ts");
if (!scanner.canHandle(targetFile)) {
throw new Error(`Scanner cannot handle file: ${targetFile}`);
}
try {
const result = await scanner.scanFile(targetFile);
console.log(`Language detected: ${result.language}`);
console.log(`Exported elements found: ${result.elements.length}`);
for (const element of result.elements) {
console.log(`\n[${element.kind.toUpperCase()}] ${element.name} (line ${element.line})`);
console.log(` Signature : ${element.signature}`);
console.log(` Returns : ${element.returnType}`);
if (element.parameters.length > 0) {
console.log(` Params : ${element.parameters.map((p) => p.name).join(", ")}`);
}
}
// Typical next step: pass elements to your doc generator
// await generateDocs(result.elements, { outputDir: "./content/docs" });
} catch (err) {
console.error("Failed to scan file:", err);
process.exit(1);
}
}
main();
// Expected output:
// Language detected: typescript
// Exported elements found: 2
//
// [FUNCTION] createUser (line 12)
// Signature : async createUser(email: string, role: UserRole): Promise<User>
// Returns : Promise<User>
// Params : email, role
//
// [TYPE] UserRole (line 4)
// Signature : type UserRole = "admin" | "member" | "viewer"
// Returns : never