Skip to content

csv-stringify Browser ESM entry points pollute global types with Node types #476

Description

@Peter-Schorn

Describe the bug

I'm using csv-stringify in a browser-based Vite + React + Typescript project. The package provides a browser-only entry point: (import { stringify } from "csv-stringify/browser/esm";), but that entry point includes the following TypeScript triple-slash directive in csv-stringify/dist/esm/index.d.ts:

/// <reference types="node" />

This injects NodeJS ambient types into the consumer’s global type environment, even though the entry point is intended for browser usage only. This can lead to typescript type-checking errors caused by unexpected ambient overload resolution.

To Reproduce

Create a Vite + React + Typescript project as follows and install csv-stringify:

npm create vite@8.2.0 csv-stringify-bug -- --template react-ts --no-interactive
cd csv-stringify-bug
npm i csv-stringify

in src/main.tsx, add the following:

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

+ import { stringify } from "csv-stringify/browser/esm";
+
+ const timeout: number = setTimeout(() => {
+     console.log('This is a timeout log message.');
+ }, 1000);
+
+ // prevent unused variable errors
+ console.log(timeout);
+ console.log(stringify);

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

Run npm run build:

% npm run build      

> csv-stringify-bug@0.0.0 build
> tsc -b && vite build

src/main.tsx:8:7 - error TS2322: Type 'Timeout' is not assignable to type 'number'.

8 const timeout: number = setTimeout(() => {
        ~~~~~~~


Found 1 error.

As indicated above, I get a type checking error because typescript is using the declaration for setTimeout defined in @types/node, which has a return type of NodeJS.Timeout, whereas the browser version has a return type of number. I am aware that it is possible to fix this issue by simply prefixing setTimeout with window, but a browser-only entry point should not require consumers to defensively qualify DOM globals to avoid Node types that should never have been in scope in the first place.

Additional context

The default Vite + React + TypeScript template uses project references and includes a separate tsconfig.node.json which has "types": ["node"] so that vite.config.ts can be written in TypeScript and use Node.js APIs. As a result, many browser-only Vite projects legitimately include Node types for tooling purposes, even though application source files are intended to be DOM-only. For example, I have the following vite.config.ts in another project that uses the node process API:

import { defineConfig, type PluginOption } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig() => {

    const isHTTPS = process.env.HTTPS_ENABLED === "1";

    return {
        plugins: [
            react()
        ],
        server: {
            open: "/scans/schornpe",
            host: true,
            https: isHTTPS ? {
                key: "/Users/pschorn/Certificates/localhost.key",
                cert: "/Users/pschorn/Certificates/localhost.crt"
            } : undefined
        },
    };
});

Activity

  1. wdavidw commented on Jan 26, 2026

    @wdavidw
    Member

    Note sure how/where to fix this, have you started to investigate ?

  2. Peter-Schorn commented on Jan 26, 2026

    @Peter-Schorn
    Author

    I have fixed it for my use case simply by removing /// <reference types="node" /> from node-csv/packages/csv-stringify/dist/esm/index.d.ts. This is what told typescript to make node types globably available, including in all consuming projects. You will need to repeat this process for any other package with a browser entry point that references—either directly or indirectly—a file with /// <reference types="node" />.

    Unless I'm missing something, you cannot include this triple slash directive in an entry point (or any depdendency thereof) meant for browsers because it will always inject node types into the global scope. And there is no way to override this by customizing your tsconfig. For example, setting compilerOptions.types to [] does not override types included via triple-slash directives.

  3. Peter-Schorn commented on Jan 26, 2026

    @Peter-Schorn
    Author

    I've only tested this change for the browser entry points, so I don't know how this would interact with the node entry points. Also, obviously a real fix would require making a change to the lib not the dist, but I don't know enough about your build process to fix this.

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