Skip to content

Remote declaration resolution #65355

Open

Description

@alshdavid

🔍 Search Terms

I often develop applications for the web without a bundler - just using browser ESM and JIT type stripping by the web server.

This setup is toolless - so no npm vite *pack next or anything. Just TypeScript, CSS and a brower.

The wrinkle is that TypeScript does not support remote declaration types which forces me to have to do an npm install despite only needing types. node_modules then fills up with half a million runtime dependencies I need to maintain in audits despite never using.

There is also no practical way to vendor declaration files for packages with TypeScript (I have tried API-extractor and even recursing through a package + its dependencies to copy out the declaration files and rename the imports to relative file-paths).

My current workflow uses browser importmap and Deno's LSP [example] (Deno is only used for type checking and nothing runtime related because it has support for remote declaration files).

<!doctype html>
<html lang="en">
  <head>
    <title>Document</title>
    <script type="importmap">
      {
        "imports": {
          "preact": "https://esm.sh/preact@11",
          "preact/hooks": "https://esm.sh/preact@11/hooks",
          "preact/jsx-runtime": "https://esm.sh/preact@11/jsx-runtime",
          "preact/compat": "https://esm.sh/preact@11/compat",
          "preact/devtools": "https://esm.sh/preact@11/devtools"
        }
      }
    </script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="./main.tsx"></script>
  </body>
</html>
// deno.json
{
  "compilerOptions": {
    "strict": true,
    "lib": ["dom","dom.iterable","esnext"],
    "jsx": "react",
    "jsxFactory": "h"
  },
  "imports": {
    "preact": "https://esm.sh/preact@11",
    "preact/hooks": "https://esm.sh/preact@11/hooks",
    "preact/jsx-runtime": "https://esm.sh/preact@11/jsx-runtime",
    "preact/compat": "https://esm.sh/preact@11/compat",
    "preact/devtools": "https://esm.sh/preact@11/devtools"
  }
}

Deno's type checker gets declarations from the x-typescript-types header.

Example; https://esm.sh/preact@11 give you https://esm.sh/preact@11.1.1/src/index.d.ts

With Deno going end of life - that's a significant loss of function for pretty much all of my workflows.

✅ Viability Checklist

⭐ Suggestion

It would be amazing if TypeScript added remote declaration resolution to tsconfig,json

{
    "compilerOptions": {
        "lib": ["DOM", "ESNext"],
        "module": "nodenext",
        "moduleResolution": "nodenext",
        "types": [],
        "allowImportingTsExtensions": true,
        "noEmit": true,
        "jsx": "react",
        "jsxFactory": "h",
        "remoteDeclarations": true,
        "paths": {
            "preact": ["https://esm.sh/preact@11"],
            "preact/hooks": ["https://esm.sh/preact@11/hooks"],
            "preact/jsx-runtime": ["https://esm.sh/preact@11/jsx-runtime"],
            "preact/compat": ["https://esm.sh/preact@11/compat"],
            "preact/devtools": ["https://esm.sh/preact@11/devtools"]
        }
    }
}

Or even

{
  "compilerOptions": {
    "paths": {
      "preact": ["https://esm.sh/preact@11.1.1/src/index.d.ts"],
      //...
    }
  }
}

Or even

{
  "compilerOptions": {
    "remoteDeclarations": {
      "preact": "https://esm.sh/preact@11.1.1/src/index.d.ts",
      //...
    }
  }
}

📃 Motivating Example

Developing a frontend application and server scripts with no npm or build tools.

Very performance sensitive applications obviously can't do this - but 95% of the applications I build are absolutely fine tool-less.

Honestly, there are bigger sins in the FE space than shipping unbundled ESM - plus you can mitigate a lot of the performance issues with some good design decisions. I'm surprised it's not more common tbh.

💻 Use Cases

  1. Developing a frontend application with no build tools.
  2. Embedding a frontend application within a native Go/Rust application as TypeScript without needing a build step that requires a separate build pipeline, copying dist files into the backend, proxies for development, etc.
  3. Writing node based shell scripts without an npm install for @node/types. Simply add an /// <include at the top of the file or from the tsconfig

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions