TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

Types declared in types.d.ts not included in output of tsc --emitDeclarationOnly

Open gthb opened this issue 1 year ago • 8 comments

Bug Report

🔎 Search Terms

emitDeclarationOnly allowJs "missing types" ".d.ts file" "declaration file"

🕗 Version & Regression Information

When did you start seeing this bug occur?

Only noticed it just now.

  • This is the behavior in every version I tried (typescript@latest, typescript@next, [email protected]), and I reviewed the FAQ for entries about declaration files

⏯ Playground Link

Workbench Repro

💻 Code

// index.js
function bar () {
  return 'baz';
}

/** @returns {BarFn} */
export function foo () {
  return bar;
}

// types.d.ts
type BarFn = () => string;

// jsconfig.json
{
  "compilerOptions": {
    "target": "es2021",
    "module": "es2022",
    "checkJs": true,
    "moduleResolution": "node",
    "strictNullChecks": true
  },
  "include": [
    "index.js",
    "types.d.ts"
  ]
}

// package.json
{
  "name": "tsc-emitdeclarationonly-js",
  "type": "module",
  "main": "index.js",
  "types": "types/lib/index.d.ts",
  "scripts": {
    "gentypes": "tsc -p jsconfig.json --noEmit false --declaration --emitDeclarationOnly --outDir types"
  },
  "devDependencies": {
    "typescript": "^4.9.3"
  }
}

🙁 Actual behavior

The command tsc -p jsconfig.json --noEmit false --declaration --emitDeclarationOnly --outDir types generates this types/index.d.ts:

/** @returns {BarFn} */
export function foo(): BarFn;

which is missing the declaration of BarFn.

🙂 Expected behavior

I expected the BarFn declaration to be included in the generated types/index.d.ts:

/** @returns {BarFn} */
export function foo(): BarFn;
export type BarFn = () => string;

... as indeed it is if I move the declaration from types.d.ts into index.js, expressed in JSDoc form:

/** @typedef {() => string} BarFn */

... but I don't want it there and in JSDoc form, I want it in a types.d.ts file (visible across my codebase) and in TypeScript form. And included in the generated types/index.d.ts.

gthb avatar Dec 06 '22 16:12 gthb

By design. This is essentially a duplicate of #32624.

MartinJohns avatar Dec 06 '22 16:12 MartinJohns

Thanks. So if I have declared some types in a .d.ts file to make them global to my JS project, and want to also publish them as part of my project's public interface ... then that's simply out of scope for tsc and I should just copy over that additional .d.ts file myself. Correct?

gthb avatar Dec 06 '22 17:12 gthb

copy over that additional .d.ts file myself

... hm, and edit the generated index.d.ts file to explicitly import types from that types.d.ts file? The type names referenced in the generated index.d.ts file do not resolve unless I add an explicit import like:

import { BarFn } from "./types.d.ts";

Is that also as designed? It seems contrary to what happens inside the JS project — types in types.d.ts are globally resolvable in all .js files in the project, but then when referenced in the generated .d.ts file they are not resolvable without manually editing that generated file. 😵

Sorry about the basic questions, I have the feeling I'm missing some intended step or config that makes this make sense. :-)

gthb avatar Dec 06 '22 17:12 gthb

If you had a .d.ts file which you consumed and you published it along with your package, and someone else did the same thing, then you'd have a conflict in the global scope.

In general if you're hand-authoring something that's local to your project, it should be in a .ts file, not a .d.ts file.

RyanCavanaugh avatar Dec 07 '22 00:12 RyanCavanaugh

[I'm almost there!]

If you had a .d.ts file which you consumed and you published it along with your package, and someone else did the same thing, then you'd have a conflict in the global scope.

Here you mean specifically a .d.ts file with no top-level imports or exports (so it isn't describing a module, so its declarations go into the global scope) ... is that correct?

In contrast, a module .d.ts. file is OK to publish and consume internally (just have to use import('...my-module').MyType instead of MyType) ... correct?

In general if you're hand-authoring something that's local to your project, it should be in a .ts file, not a .d.ts file.

Hm, but if that .ts file contains only type declarations, at least one of which is exported (so the file describes a module), then that is exactly the same thing — the only difference in practice is:

  • if the file is named types.d.ts then tsc will not emit it to outDir (so imports from it in files that are emitted will be broken)
  • if the file is named types.ts, then tsc will emit it to outDir, with the name types.d.ts and identical contents (except for possible formatting differences)

... correct? Or are there other differences?

(In my use case, this file should only contain types, no implementation, and I prefer the .d.ts name in order to both convey and enforce that.)

Then the above example becomes:

// index.js
function bar () {
  return 'baz';
}

/** @returns {import("./types").BarFn} */
export function foo () {
  return bar;
}

// types.d.ts
export type BarFn = () => string;

Workbench Repro

Here I can either rename types.d.ts to types.ts ... or equivalently just copy it to the outDir after running tsc ... and either way, that's it, I've published my types, and caused no problems. All correct?

gthb avatar Dec 08 '22 15:12 gthb

:wave: Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript.


Issue body code block by @gthb

:x: Failed: -

  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • Declaration or statement expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • Declaration or statement expected.
  • ';' expected.
  • ';' expected.
Historical Information
Version Reproduction Outputs
4.5.2, 4.6.2, 4.7.2, 4.8.2, 4.9.3

:x: Failed: -

  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • Left side of comma operator is unused and has no side effects.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • Declaration or statement expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • ';' expected.
  • Declaration or statement expected.
  • ';' expected.
  • ';' expected.

typescript-bot avatar Dec 09 '22 08:12 typescript-bot

:wave: Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of this repro running against the nightly TypeScript.


Comment by @gthb

:+1: Compiled

Historical Information
Version Reproduction Outputs
4.5.2, 4.6.2, 4.7.2, 4.8.2, 4.9.3

:+1: Compiled

typescript-bot avatar Dec 09 '22 08:12 typescript-bot

An external tool generates .d.ts and .js files, which I can't seem to get to work with tsc. I tried renaming the .d.ts file to a .ts file as suggested in this thread, and it doesn't compile:

.d.ts file has:

interface IUpdateServerService { value: string }
export const UpdateServerService: IUpdateServerService;

As a .ts file, the compiler error I get is:

error TS1155: 'const' declarations must be initialized.

It would be nice if tsc could be configured to just directly copy over the .d.ts and .js files.

sahendrickson avatar Jan 16 '23 07:01 sahendrickson

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow or the TypeScript Discord community.

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow or the TypeScript Discord community.