Skip to content

feat: implement resolveModuleNames for type resolution in ERB virtual files#29

Merged
kudoas merged 6 commits intomainfrom
copilot/fix-intersection-types-erb
Feb 9, 2026
Merged

feat: implement resolveModuleNames for type resolution in ERB virtual files#29
kudoas merged 6 commits intomainfrom
copilot/fix-intersection-types-erb

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 9, 2026

Problem

JSDoc intersection types and imported type definitions resolve to any in ERB javascript_tag blocks, despite working correctly in regular .js files. Example:

/**
 * @typedef {import('./types').User} User
 * @typedef {import('./types').Address} Address
 * @typedef {User & Address} UserWithAddress
 */

/** @type {UserWithAddress} */
const person = { ... };
person. // ← Completions show `any` instead of merged properties

Root cause: The Language Service Host lacks resolveModuleNames, preventing TypeScript from resolving external type imports in virtual files.

Changes

  • Added resolveModuleNames to LanguageServiceHost in typescriptCompletionService.ts

    • Uses TypeScript's resolveModuleName API with sys.fileExists and sys.readFile
    • Enables standard module resolution for virtual JavaScript extracted from ERB blocks
  • Added test coverage validating intersection type resolution with external imports

    • Verifies completions include properties from all intersected types
    • Tests basic completions, hover info, and version tracking

Implementation

resolveModuleNames: (moduleNames, containingFile) => {
  return moduleNames.map((moduleName) => {
    const result = ts.resolveModuleName(moduleName, containingFile, this.compilerOptions, {
      fileExists: sys.fileExists,
      readFile: sys.readFile
    });
    return result.resolvedModule;
  });
}

This matches TypeScript's standard Language Service Host pattern for module resolution.

Original prompt

This section details on the original issue you should resolve

<issue_title>fix: Resolve intersection types as any in ERB virtual files – implement resolveModuleNames</issue_title>
<issue_description>## 🐛 Intersection type resolves to any in ERB files

Summary

When using JSDoc intersection types or external type imports in ERB files, type information resolves to any. In standard .js files, this works as expected, but it fails in ERB block environments.

Root Cause

  • The TypeScript Language Service Host lacks an implementation for resolveModuleNames, so the virtual JS file cannot resolve imported type definitions correctly.
  • Intersection types require TypeScript to resolve every referenced type (via import), but currently only the virtual file is tracked and resolved by the language service.
  • This causes missing type information and fallback to any.

Steps to Reproduce

  1. Add a JSDoc intersection type via import in an ERB file block.
  2. Observe that type completions resolve to any.
  3. In a regular JS file, everything works as expected.

Solution Plan

1. Add resolveModuleNames to the LanguageServiceHost

  • Use TypeScript's resolveModuleName API
  • Provide sys.fileExists and sys.readFile for resolution, matching TypeScript's standard logic

Example Implementation

resolveModuleNames: (moduleNames, containingFile) => {
  return moduleNames.map(moduleName => {
    const result = ts.resolveModuleName(
      moduleName,
      containingFile,
      this.compilerOptions,
      {
        fileExists: sys.fileExists,
        readFile: sys.readFile
      }
    );
    return result.resolvedModule;
  });
},

2. Optionally add getCanonicalFileName and useCaseSensitiveFileNames if needed


Expected Result

  • Imported type definitions are resolved properly from ERB virtual files, and intersection types work as expected with full type completion.

Notes

  • Changing checkJs (true/false) does not resolve the core issue. The main problem is the missing hooks for type resolution.
  • Interface-based inheritance works only because TypeScript can partially resolve, but intersection types depend on full external resolution.

This fix is essential for robust type completions in ERB blocks. Please review! 🚀</issue_description>

<agent_instructions>create draft pull request</agent_instructions>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 4 commits February 9, 2026 13:21
…iles

Co-authored-by: kudoas <45157831+kudoas@users.noreply.github.com>
…virtual files

Co-authored-by: kudoas <45157831+kudoas@users.noreply.github.com>
Co-authored-by: kudoas <45157831+kudoas@users.noreply.github.com>
Co-authored-by: kudoas <45157831+kudoas@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix intersection types resolving to any in ERB files fix: implement resolveModuleNames for type resolution in ERB virtual files Feb 9, 2026
Copilot AI requested a review from kudoas February 9, 2026 13:27
@kudoas kudoas marked this pull request as ready for review February 9, 2026 13:30
@kudoas kudoas merged commit 87df3b1 into main Feb 9, 2026
2 checks passed
@kudoas kudoas changed the title fix: implement resolveModuleNames for type resolution in ERB virtual files feat: implement resolveModuleNames for type resolution in ERB virtual files Feb 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Resolve intersection types as any in ERB virtual files – implement resolveModuleNames

2 participants