Skip to content

Use authored types.ts for type-only APIs during TypeScript migration - #1481

Merged
sergey-shandar merged 42 commits into
mainfrom
agent/jsdoc-type-only-imports
Aug 10, 2026
Merged

Use authored types.ts for type-only APIs during TypeScript migration#1481
sergey-shandar merged 42 commits into
mainfrom
agent/jsdoc-type-only-imports

Conversation

@sergey-shandar

@sergey-shandar sergey-shandar commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • use real authored types.ts for type-level APIs that should remain TypeScript
  • reference the same physical types.ts source from both TypeScript import type and JSDoc @import
  • keep runtime implementation migration separate from type-only TypeScript source
  • treat declaration-only modules such as fjs/types/phantom/module.f.ts as types.ts cases instead of inventing JavaScript runtime values
  • keep migrated JavaScript free of runtime dependencies on remaining implementation .ts / .f.ts
  • validate types.ts under TypeScript, Deno, Bun, package emit, and clean consumers before the first real migration
  • leave skipLibCheck and the generated-declaration .gitignore rules unchanged

Design

A directory may split runtime and type source:

types.ts
module.f.ts

During Stage 1:

types.ts
module.f.mjs

and later:

types.ts
module.f.js

Both source languages reference the real file:

import type { Phantom } from './types.ts'
/** @import { Phantom } from './types.ts' */

Unlike the authored-.d.ts experiments, this does not depend on TypeScript resolving a missing .ts / .js path to a declaration file. Deno can resolve the same physical types.ts source.

A type-only FunctionalScript source should therefore become types.ts. For example:

fjs/types/phantom/module.f.ts
    ->
fjs/types/phantom/types.ts

types.ts may contain constructs such as type, interface, type-only imports/exports, declare const, and unique symbol; no artificial runtime Symbol() or other runtime API is required.

Packaging validation

types.ts is ordinary TypeScript source, so no skipLibCheck change or .gitignore exception is required.

The package prerequisite must determine the emitted/package form rather than assume it. With rewriteRelativeImportExtensions: true, validate:

  • declaration specifiers emitted from both .ts and .mjs
  • generated types.js / types.d.ts
  • TypeScript, Node, Deno, and Bun clean consumers
  • whether the runtime-emission pass must remain while authored types.ts exists

Do not simplify the package pipeline until that experiment establishes the minimal portable layout.

Scope

This PR defines the migration convention and validation requirements. It does not yet rename production type modules; those changes can be exercised first on an experimental branch.

Validation

  • Deno experiments showed that missing types.ts and types.js specifiers do not resolve to sibling authored .d.ts files
  • the migration/package/test plans now use a real authored types.ts source path
  • the special authored-types.d.ts .gitignore exception was removed
  • the skipLibCheck change was removed because types.ts participates in normal TypeScript source checking
  • package behavior is intentionally left as an explicit fixture/experiment requirement before production migration

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
functionalscript 007cda7 Commit Preview URL

Branch Preview URL
Aug 10 2026, 10:37 PM

@sergey-shandar sergey-shandar changed the title Allow JSDoc type-only imports during TS migration Use authored types.d.ts during TypeScript migration Aug 10, 2026
@sergey-shandar sergey-shandar changed the title Use authored types.d.ts during TypeScript migration Use checked authored types.d.ts during TypeScript migration Aug 10, 2026
@sergey-shandar

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5eb4136c51

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fjs/ci/todo/f-mjs-package-support.md Outdated
@sergey-shandar

Copy link
Copy Markdown
Contributor Author

@codex review

@sergey-shandar
sergey-shandar marked this pull request as ready for review August 10, 2026 22:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db2de1c054

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fjs/fsc/README.md Outdated

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything committed here checks out. One bullet in the Validation section doesn't reproduce for me, but nothing in the repository depends on it.

The two risky mechanical changes are both correct

skipLibCheck off. This is the one that could have gone badly — dropping it means TypeScript also checks every .d.ts under node_modules, including @types/node. npx tsc --noEmit on the branch reports 0 errors, so the ambient surface is clean at this pin. Leaving the line commented at its default rather than setting false explicitly matches how the rest of the file records defaults.

The .gitignore negation. !**/types.d.ts after **/*.d.ts does exactly what's intended and nothing more:

file ignored
types.d.ts no — tracked
module.f.d.ts yes
module.f.d.mts yes

Worth confirming explicitly because a negation placed against a broader directory rule silently fails, and generated declarations leaking into the repo would be an unpleasant way to find out.

The ./types.ts specifier works from both sides

Built a fixture with an authored types.d.ts and a deliberate type error in each consumer:

consumer.f.ts (2,39)  error TS2322: Type 'string' is not assignable to type 'number'   // import type … from './types.ts'
consumer.f.mjs(5,23)  error TS2322: Type 'string' is not assignable to type 'number'   // @import … from './types.ts'

So the specifier resolves to the authored declaration from both import type and JSDoc @import, and the resulting types are genuinely checked rather than degrading to any. npm test — tsc clean, 2356 pass / 0 fail.

The one claim I can't reproduce

verified that direct JSDoc @import of ./types.d.ts is rejected, which is why the convention uses ./types.ts

It isn't rejected here — it resolves and checks identically. Same fixture, ./types.d.ts versus ./types.ts side by side:

direct.f.mjs (3,22)  error TS2322: Type 'string' is not assignable to type 'number'   // @import … from './types.d.ts'
control.f.mjs(3,22)  error TS2322: Type 'string' is not assignable to type 'number'   // @import … from './types.ts'

Same from TypeScript — import type { P } from './types.d.ts' in a .f.ts also resolves and flags the planted error. Both forms behave the same under this repo's allowImportingTsExtensions + rewriteRelativeImportExtensions on TS 7.0.2, so if the rejection was observed it was under some other configuration.

This doesn't change my read of the decision. ./types.ts is still the right specifier for the stated reason that is load-bearing — one form that reads identically from TypeScript and JSDoc and stays unchanged across module.f.ts -> module.f.mjs -> module.f.js. And happily, AGENTS.md and the migration doc justify it exactly that way and never assert the .d.ts form is rejected, so the committed guidance is accurate as written. It's only the PR description that would send someone looking for a rejection that isn't there.

Approving.

@sergey-shandar

Copy link
Copy Markdown
Contributor Author

Everything committed here checks out. One bullet in the Validation section doesn't reproduce for me, but nothing in the repository depends on it.

The two risky mechanical changes are both correct

skipLibCheck off. This is the one that could have gone badly — dropping it means TypeScript also checks every .d.ts under node_modules, including @types/node. npx tsc --noEmit on the branch reports 0 errors, so the ambient surface is clean at this pin. Leaving the line commented at its default rather than setting false explicitly matches how the rest of the file records defaults.

The .gitignore negation. !**/types.d.ts after **/*.d.ts does exactly what's intended and nothing more:

file ignored
types.d.ts no — tracked
module.f.d.ts yes
module.f.d.mts yes
Worth confirming explicitly because a negation placed against a broader directory rule silently fails, and generated declarations leaking into the repo would be an unpleasant way to find out.

The ./types.ts specifier works from both sides

Built a fixture with an authored types.d.ts and a deliberate type error in each consumer:

consumer.f.ts (2,39)  error TS2322: Type 'string' is not assignable to type 'number'   // import type … from './types.ts'
consumer.f.mjs(5,23)  error TS2322: Type 'string' is not assignable to type 'number'   // @import … from './types.ts'

So the specifier resolves to the authored declaration from both import type and JSDoc @import, and the resulting types are genuinely checked rather than degrading to any. npm test — tsc clean, 2356 pass / 0 fail.

The one claim I can't reproduce

verified that direct JSDoc @import of ./types.d.ts is rejected, which is why the convention uses ./types.ts

It isn't rejected here — it resolves and checks identically. Same fixture, ./types.d.ts versus ./types.ts side by side:

direct.f.mjs (3,22)  error TS2322: Type 'string' is not assignable to type 'number'   // @import … from './types.d.ts'
control.f.mjs(3,22)  error TS2322: Type 'string' is not assignable to type 'number'   // @import … from './types.ts'

Same from TypeScript — import type { P } from './types.d.ts' in a .f.ts also resolves and flags the planted error. Both forms behave the same under this repo's allowImportingTsExtensions + rewriteRelativeImportExtensions on TS 7.0.2, so if the rejection was observed it was under some other configuration.

This doesn't change my read of the decision. ./types.ts is still the right specifier for the stated reason that is load-bearing — one form that reads identically from TypeScript and JSDoc and stays unchanged across module.f.ts -> module.f.mjs -> module.f.js. And happily, AGENTS.md and the migration doc justify it exactly that way and never assert the .d.ts form is rejected, so the committed guidance is accurate as written. It's only the PR description that would send someone looking for a rejection that isn't there.

Approving.

We've changed the design, again.

@sergey-shandar

Copy link
Copy Markdown
Contributor Author

@codex review

@sergey-shandar sergey-shandar changed the title Use checked authored types.d.ts during TypeScript migration Use authored types.ts for type-only APIs during TypeScript migration Aug 10, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 0dcc76c808

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@sergey-shandar
sergey-shandar added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit f59e949 Aug 10, 2026
19 checks passed
@sergey-shandar
sergey-shandar deleted the agent/jsdoc-type-only-imports branch August 10, 2026 22:48
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.

2 participants