Use authored types.ts for type-only APIs during TypeScript migration - #1481
Conversation
Deploying with
|
| 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 |
types.d.ts during TypeScript migration
types.d.ts during TypeScript migrationtypes.d.ts during TypeScript migration
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
@codex review |
There was a problem hiding this comment.
💡 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".
o2alexanderfedin
left a comment
There was a problem hiding this comment.
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
@importof./types.d.tsis 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. |
|
@codex review |
types.d.ts during TypeScript migrationtypes.ts for type-only APIs during TypeScript migration
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Summary
types.tsfor type-level APIs that should remain TypeScripttypes.tssource from both TypeScriptimport typeand JSDoc@importfjs/types/phantom/module.f.tsastypes.tscases instead of inventing JavaScript runtime values.ts/.f.tstypes.tsunder TypeScript, Deno, Bun, package emit, and clean consumers before the first real migrationskipLibCheckand the generated-declaration.gitignorerules unchangedDesign
A directory may split runtime and type source:
During Stage 1:
and later:
Both source languages reference the real file:
/** @import { Phantom } from './types.ts' */Unlike the authored-
.d.tsexperiments, this does not depend on TypeScript resolving a missing.ts/.jspath to a declaration file. Deno can resolve the same physicaltypes.tssource.A type-only FunctionalScript source should therefore become
types.ts. For example:types.tsmay contain constructs such astype,interface, type-only imports/exports,declare const, andunique symbol; no artificial runtimeSymbol()or other runtime API is required.Packaging validation
types.tsis ordinary TypeScript source, so noskipLibCheckchange or.gitignoreexception is required.The package prerequisite must determine the emitted/package form rather than assume it. With
rewriteRelativeImportExtensions: true, validate:.tsand.mjstypes.js/types.d.tstypes.tsexistsDo 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
types.tsandtypes.jsspecifiers do not resolve to sibling authored.d.tsfilestypes.tssource pathtypes.d.ts.gitignoreexception was removedskipLibCheckchange was removed becausetypes.tsparticipates in normal TypeScript source checking