A simple parser for React properties defined in TypeScript instead of propTypes
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Direct access to the core Parser class for advanced use cases requiring fine-grained control over component analysis and custom TypeScript program integration.
The core parser class that performs TypeScript AST analysis and component documentation extraction.
/**
* Core parser class for extracting component documentation
* @param program - TypeScript program instance
* @param opts - Parser configuration options
*/
class Parser {
constructor(program: ts.Program, opts: ParserOptions);
}Usage Examples:
import { Parser } from "react-docgen-typescript";
import * as ts from "typescript";
// Create TypeScript program
const program = ts.createProgram(
["./src/Button.tsx"],
{
jsx: ts.JsxEmit.React,
target: ts.ScriptTarget.ES2019
}
);
// Create parser instance
const parser = new Parser(program, {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => !prop.name.startsWith('_')
});Extract documentation for a specific component symbol.
/**
* Extract component documentation from TypeScript symbol
* @param exp - TypeScript symbol representing the component
* @param source - Source file containing the component
* @param componentNameResolver - Optional custom name resolver
* @param customComponentTypes - Optional custom component type names
* @returns Component documentation or null if not a valid component
*/
getComponentInfo(
exp: ts.Symbol,
source: ts.SourceFile,
componentNameResolver?: ComponentNameResolver,
customComponentTypes?: string[]
): ComponentDoc | null;Usage Examples:
import { Parser } from "react-docgen-typescript";
import * as ts from "typescript";
const parser = new Parser(program, {});
const sourceFile = program.getSourceFile("./src/Button.tsx");
const checker = program.getTypeChecker();
if (sourceFile) {
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
if (moduleSymbol) {
const exports = checker.getExportsOfModule(moduleSymbol);
exports.forEach(exportSymbol => {
const componentDoc = parser.getComponentInfo(
exportSymbol,
sourceFile,
// Custom name resolver
(exp, source) => {
if (exp.getName() === "default") {
return "CustomButton";
}
return undefined;
},
// Custom component types
["MyCustomComponent"]
);
if (componentDoc) {
console.log("Found component:", componentDoc.displayName);
}
});
}
}Extract detailed property information from a props symbol.
/**
* Extract props documentation from TypeScript symbol
* @param propsObj - TypeScript symbol representing props interface/type
* @param defaultProps - Optional default prop values
* @returns Props documentation object
*/
getPropsInfo(
propsObj: ts.Symbol,
defaultProps?: StringIndexedObject<string>
): Props;Usage Examples:
import { Parser } from "react-docgen-typescript";
const parser = new Parser(program, {
savePropValueAsString: true
});
// Assuming you have a props symbol from component analysis
const propsInfo = parser.getPropsInfo(propsSymbol, {
variant: "primary",
size: "medium"
});
Object.entries(propsInfo).forEach(([propName, propInfo]) => {
console.log(`${propName}: ${propInfo.type.name}`);
console.log(` Required: ${propInfo.required}`);
console.log(` Description: ${propInfo.description}`);
if (propInfo.defaultValue) {
console.log(` Default: ${propInfo.defaultValue.value}`);
}
});Detect and extract props from different component patterns.
/**
* Extract props symbol from functional component type
* @param type - TypeScript type of the component
* @returns Props symbol or null if not a functional component
*/
extractPropsFromTypeIfStatelessComponent(type: ts.Type): ts.Symbol | null;
/**
* Extract props symbol from class component type
* @param type - TypeScript type of the component
* @returns Props symbol or null if not a class component
*/
extractPropsFromTypeIfStatefulComponent(type: ts.Type): ts.Symbol | null;Usage Examples:
import { Parser } from "react-docgen-typescript";
const parser = new Parser(program, {});
const checker = program.getTypeChecker();
// For a component symbol
const componentType = checker.getTypeOfSymbolAtLocation(
componentSymbol,
componentSymbol.valueDeclaration!
);
// Try extracting as functional component
let propsSymbol = parser.extractPropsFromTypeIfStatelessComponent(componentType);
if (!propsSymbol) {
// Try extracting as class component
propsSymbol = parser.extractPropsFromTypeIfStatefulComponent(componentType);
}
if (propsSymbol) {
const propsInfo = parser.getPropsInfo(propsSymbol);
console.log("Props found:", Object.keys(propsInfo));
}import { Parser } from "react-docgen-typescript";
import * as ts from "typescript";
// Create program with custom options
const compilerOptions: ts.CompilerOptions = {
jsx: ts.JsxEmit.ReactJSX,
target: ts.ScriptTarget.ES2020,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
allowSyntheticDefaultImports: true,
esModuleInterop: true
};
const filePaths = ["./src/components/*.tsx"];
const program = ts.createProgram(filePaths, compilerOptions);
// Custom parser with advanced options
const parser = new Parser(program, {
propFilter: (prop, component) => {
// Advanced filtering logic
return !prop.name.startsWith('data-') &&
!prop.parent?.fileName.includes('node_modules');
},
componentNameResolver: (exp, source) => {
// Advanced name resolution
const fileName = source.fileName.split('/').pop()?.replace('.tsx', '');
if (exp.getName() === 'default' && fileName) {
return fileName.charAt(0).toUpperCase() + fileName.slice(1);
}
return undefined;
},
shouldExtractLiteralValuesFromEnum: true,
shouldExtractValuesFromUnion: true,
customComponentTypes: ['StyledComponent', 'MotionComponent']
});import { Parser } from "react-docgen-typescript";
const parser = new Parser(program, parserOptions);
const sourceFiles = program.getSourceFiles()
.filter(sf => sf.fileName.includes('/components/'));
const allComponentDocs: ComponentDoc[] = [];
sourceFiles.forEach(sourceFile => {
const checker = program.getTypeChecker();
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
if (moduleSymbol) {
const exports = checker.getExportsOfModule(moduleSymbol);
exports.forEach(exportSymbol => {
const componentDoc = parser.getComponentInfo(exportSymbol, sourceFile);
if (componentDoc) {
allComponentDocs.push(componentDoc);
}
});
}
});
console.log(`Found ${allComponentDocs.length} components`);interface StringIndexedObject<T> {
[key: string]: T;
}
type ComponentNameResolver = (
exp: ts.Symbol,
source: ts.SourceFile
) => string | undefined | null | false;The Parser class methods can encounter various error conditions:
getComponentInfo returns null for invalid componentsgetPropsInfo returns empty object for components without propsimport { Parser } from "react-docgen-typescript";
try {
const parser = new Parser(program, options);
const componentDoc = parser.getComponentInfo(symbol, sourceFile);
if (componentDoc === null) {
console.log("Symbol is not a valid React component");
}
} catch (error) {
console.error("Parser creation failed:", error.message);
}Install with Tessl CLI
npx tessl i tessl/npm-react-docgen-typescript