diff --git a/fixtures/input/example-0001.ts b/fixtures/input/example-0001.ts index 9061b51..4920b69 100644 --- a/fixtures/input/example-0001.ts +++ b/fixtures/input/example-0001.ts @@ -329,16 +329,16 @@ export const complexArrays = { // } // } -// TODO: Default Type Parameters -// export interface DefaultGeneric< -// T = string, -// K extends keyof any = string, -// V extends Record = Record -// > { -// key: K -// value: T -// record: V -// } +// Default Type Parameters +export interface DefaultGeneric< + T = string, + K extends keyof any = string, + V extends Record = Record +> { + key: K + value: T + record: V +} // TODO: Method Decorators and Metadata // export const methodDecorator = ( diff --git a/fixtures/output/example-0001.d.ts b/fixtures/output/example-0001.d.ts index cb37c7d..5845097 100644 --- a/fixtures/output/example-0001.d.ts +++ b/fixtures/output/example-0001.d.ts @@ -81,7 +81,7 @@ export declare interface AuthResponse { token: string expiresIn: number } -export declare type AuthStatus = 'authenticated' | 'unauthenticated'; +export declare type AuthStatus = 'authenticated' | 'unauthenticated' export declare function authenticate(user: string, password: string): Promise; export declare const defaultHeaders: { 'Content-Type': 'application/json'; @@ -101,7 +101,7 @@ export declare interface ComplexGeneric, K ext transform: (input: T[K]) => string nested: Array> } -export declare type ComplexUnionIntersection = +export declare type ComplexUnionIntersection = | (User & { role: 'admin' }) | (Product & { category: string }) & { @@ -120,9 +120,9 @@ export declare function processData(data: number): number; export declare function processData(data: boolean): boolean; export declare function processData(data: T): T; export declare function processData(data: unknown): unknown; -export declare type EventType = 'click' | 'focus' | 'blur'; -export declare type ElementType = 'button' | 'input' | 'form'; -export declare type EventHandler = `on${Capitalize}${Capitalize}`; +export declare type EventType = 'click' | 'focus' | 'blur' +export declare type ElementType = 'button' | 'input' | 'form' +export declare type EventHandler = `on${Capitalize}${Capitalize}` export declare type RecursiveObject = { id: string children?: RecursiveObject[] @@ -133,9 +133,18 @@ export declare const complexArrays: { matrix: Array>> | Array<'a' | 'b' | Array<'c' | 'd'>> | Array>>>; tuples: Array | Array<'literal' | 42 | false>>; }; +export declare interface DefaultGeneric< + T = string, + K extends keyof any = string, + V extends Record = Record +> { + key: K + value: T + record: V +} export declare function complexAsyncGenerator(): any; export declare function isUser(value: unknown): value is User; -export declare type UserId = string & { readonly __brand: unique symbol }; +export declare type UserId = string & { readonly __brand: unique symbol } export declare type ProductId = number & { readonly __brand: unique symbol } @@ -158,7 +167,7 @@ export declare type PolymorphicComponent

= { props: { as?: C } & Omit, keyof P> & P ): React.ReactElement | null } -export type DynamicRecord = { +export declare type DynamicRecord = { [P in K]: P extends number ? Array : P extends string diff --git a/src/extract.ts b/src/extract.ts index 424181b..9d3a7b9 100644 --- a/src/extract.ts +++ b/src/extract.ts @@ -906,41 +906,19 @@ function processFunctionDeclaration( */ function processInterfaceDeclaration(declaration: string, isExported = true): string { const lines = declaration.split('\n') - const baseIndent = getIndentation(lines[0]) - const memberIndent = `${baseIndent} ` - // Process the interface header - const firstLine = lines[0].trim() - const match = firstLine.match(/^(?:export\s+)?interface\s+([^<\s{]+)(<[^{]+>)?/) - if (!match) - return declaration - - const [, name, generics = ''] = match + // Only modify the first line to add 'declare' if needed + const firstLine = lines[0] const prefix = isExported ? 'export declare' : 'declare' - // Process interface members maintaining original indentation - const processedLines = [ - `${baseIndent}${prefix} interface ${name}${generics} {`, - ] - - // Add members with preserved indentation - let seenContent = false - for (let i = 1; i < lines.length - 1; i++) { - const line = lines[i] - const content = line.trim() - if (content) { - seenContent = true - processedLines.push(`${memberIndent}${content}`) - } - } - - // If no content was found, add a newline for better formatting - if (!seenContent) { - processedLines.push('') - } + // Replace 'export interface' or 'interface' with the correct prefix + const modifiedFirstLine = firstLine.replace( + /^(\s*)(?:export\s+)?interface/, + `$1${prefix} interface`, + ) - processedLines.push(`${baseIndent}}`) - return processedLines.join('\n') + // Return the modified first line with all other lines unchanged + return [modifiedFirstLine, ...lines.slice(1)].join('\n') } /** @@ -948,40 +926,24 @@ function processInterfaceDeclaration(declaration: string, isExported = true): st */ function processTypeDeclaration(declaration: string, isExported = true): string { const lines = declaration.split('\n') - const baseIndent = getIndentation(lines[0]) + const firstLine = lines[0].trim() - // Handle type exports - if (lines[0].includes('type {')) { + // Preserve direct type exports (export type { X }) + if (firstLine.startsWith('export type {')) { return declaration } - // Extract type name and initial content - const typeMatch = lines[0].match(/^(?:export\s+)?type\s+([^=\s]+)\s*=\s*(.*)/) - if (!typeMatch) - return declaration - - const [, name, initialContent] = typeMatch + // Only modify the first line to add 'declare' if needed const prefix = isExported ? 'export declare' : 'declare' - // If it's a simple single-line type - if (lines.length === 1) { - return `${baseIndent}${prefix} type ${name} = ${initialContent};` - } - - // For multi-line types, properly format with line breaks - const processedLines = [`${baseIndent}${prefix} type ${name} = ${initialContent.trim()}`] - const remainingLines = lines.slice(1) - - for (const line of remainingLines) { - const trimmed = line.trim() - if (trimmed) { - // Keep original indentation for the line - const lineIndent = getIndentation(line) - processedLines.push(`${lineIndent}${trimmed}`) - } - } + // Replace 'export type' or 'type' with the correct prefix + const modifiedFirstLine = lines[0].replace( + /^(\s*)(?:export\s+)?type(?!\s*\{)/, + `$1${prefix} type`, + ) - return processedLines.join('\n') + // Return the modified first line with all other lines unchanged + return [modifiedFirstLine, ...lines.slice(1)].join('\n') } function processSourceFile(content: string, state: ProcessingState): void { @@ -1447,11 +1409,6 @@ function generateOptimizedImports(state: ImportTrackingState, dtsLines: string[] return imports.sort() } -function getIndentation(line: string): string { - const match = line.match(/^(\s+)/) - return match ? match[1] : '' -} - /** * Normalize type references */