Skip to content

EPBDS-16211 E5: retire propTypes and the prop-types package - #145

Merged
AlexSamBY merged 1 commit into
masterfrom
ts-e5
Oct 1, 2026
Merged

AlexSamBY merged 1 commit into
masterfrom
ts-e5

Conversation

@AlexSamBY

Copy link
Copy Markdown
Member

What changes

E5: every propTypes declaration is gone, and so is prop-types. rg "prop-types" src returns nothing. The package is out of devDependencies, and lint rejects an import of it.

What is deleted

  • 39 propTypes blocks in 38 files. The E2 and E3 conversions kept each block on purpose, so this is one deletion rather than forty.
  • The semantic proxy components/types.ts, the barrel's PropTypes re-export, and every prop-types and type import.
  • The host's and the form layer's handling of prop types. documentHost.ts copied the class's prop types onto the function component, and withFormSetup merged them under its own. The four tests that pinned both now pin the rest of what they covered.

The descriptions survive, in the props types

  • They moved into the props types' JSDoc first, wherever a type did not carry them yet: TableView, Tabs, Upload, ProgressSteps, AutoSave, asField, asInputDateField and ToggleField.
  • UIRender's three undocumented props are documented in the published UIRender.UIRenderProps, where a host reads them: initialValues, form and embedded. The form layer's props are described in a comment where the layer reads them.
  • A script compared every commented prop with its TS declaration. What it still lists is documented by a type alias or by the published type.
  • Comments that cited propTypes as a fact are reworded: Counter, Listbox, StandaloneTabs, documentHost and the form layer.

Not done, deliberately: the vocabulary as TS aliases

The plan's E5 step 2 asked for the type proxy to be recreated as aliases, such as type Id = string and type Px = number. I measured first:

  • The proxy defined 41 names, and the converted files used 16.
  • Twelve of the 16 restate PropTypes itself: type.String, type.Method, type.Of, and so on.
  • Four carry a meaning of their own: Id (used twice), Fraction, UrlOrBase64OrPreview and NumberOrString.
  • E2 and E3 already wrote that meaning into the props' JSDoc, for example "Fraction from 0 to 1" and "URL to link to".

An alias of a primitive checks nothing. The vocabulary would have been the same documentation in a second place, and 25 of its names would never have been used. The plan records this decision.

No diagnostic a test relied on is lost

  • React 16 to 18 warn in development on a failed prop type. React 19 checks none.
  • Two suites allowed prop-type warnings: four entries in the examples suite, and two in the nested-row suite.
  • None of the six fired. I measured this on React 18, on master, just before the deletion. They are removed, and the nested-row suite now requires no warning at all.
  • The formProps/instance warnings that the plan's §8 flagged were not taken as the contract being met. The declaration itself was wrong. A nested Data document shares its parent's form and is rendered without either prop. EPBDS-16211: fix the bugs the modernization found, and the docs that drifted #139 made both optional.

Kept from coming back

  • prop-types is still installed, as a dependency of eslint-plugin-react, react-markdown and the React 16 fixture. An import would therefore resolve and bundle it again.
  • no-restricted-imports now rejects it in all seven src overrides. I proved this with a probe file in core, in a test and in the demo; each failed with the rule's message.
  • CLAUDE.md states the rule: no propTypes block. A component's props type, and its JSDoc, are the documentation.

Also

  • README.md no longer lists prop-types among the bundled libraries.
  • docs/SUPPORTED-PROPS.md is regenerated. Only Dropdown.tsx's line count changed, from 381 to 364.
  • The wrong colGroup propType that E3 recorded ({styles}) is gone with the rest. The TS type says style.
  • docs/UPGRADE-PLAN.md records E5 and closes its items: the §2 audit line, the §8 row, the E-checks row and the Appendix C checklist. The Appendix C command now looks for a dependency entry, since the lint rule names the package seven times.

Found, and left for a change of its own

On React 18, the examples suite's other five allowlist entries match nothing either. Whether any of them still guards something on 16, 17 or 19 is for that change to measure.

Measured

master this PR
corpus, 38 examples: commits at mount / translate calls 107 / 1012 107 / 1012
on an edit: commits / translate calls 27 / 64 27 / 64
DOM after mount and after an edit identical, all 38
dist/index.js 322,915 bytes 311,527 bytes (−11,388, −3.5%)
dist/index.js, gzipped 107,110 bytes 104,047 bytes
isRequired in dist/index.js 49 0
src/core + src/library, TS 139 files, 20,203 lines 138 files, 19,514 lines

Both bundles were built from git archive copies in the same directory. Built in the worktree, the same trees measure 322,919 and 311,529 bytes; 322,919 is the figure #144 recorded.

Gates

All exited 0, run on this branch:

  • lint:js, lint:css, typecheck, typecheck:contract;
  • docs:props:check, docs:views:check, css:fixture:check;
  • test:env-flags, build;
  • test:coverage, thresholds included;
  • test:pack, test:pack:peers, test:types;
  • test:react16, test:react17, test:react19, 2842 tests each, with no worker crash;
  • test:e2e, 41 tests;
  • the manifest contract.

The suite still has 2842 tests. Four tests, in three suites, pinned prop-type forwarding and merging. Each now pins the rest of its subject: the names, the class left as written, and the state shape.

🤖 Generated with Claude Code

Every propTypes declaration is deleted: 39 blocks in 38 files, with
the prop-types imports, the semantic `type` proxy
(components/types.ts) and the barrel's PropTypes re-export. The host
no longer forwards a class's prop types, and the form layer no longer
merges them. prop-types is out of devDependencies.

- The descriptions the blocks carried moved first, into the props
  types' JSDoc, wherever a type did not have them yet. UIRender's three
  undocumented props are documented in the published
  UIRender.UIRenderProps.
- The plan's step 2, the `type` vocabulary recreated as TS aliases, is
  not done. Of the proxy's 41 names, the converted files used 16, and
  only 4 carried a meaning beyond PropTypes' own. The JSDoc carries
  that meaning now, and an alias of a primitive checks nothing.
- Two suites allowed prop-type warnings: four entries in the examples
  suite, two in the nested-row suite. Measured on React 18 just before
  the deletion, none of the six fired. They are removed, and the
  nested-row suite now requires no warning at all.
- prop-types is still installed as a dependency of development
  packages, so an import would resolve and bundle it again.
  no-restricted-imports now rejects it in every src override. A probe
  file proved that in core, in a test and in the demo.

Found, and left for its own change: on React 18, the examples suite's
other five allowlist entries match nothing either.

Measured: the corpus is identical, with 107 / 1012 at mount, 27 / 64 on
an edit and the DOM unchanged. dist/index.js went from 322,915 to
311,527 bytes, and from 107,110 to 104,047 gzipped. isRequired occurs
49 times in master's bundle and none now.
@AlexSamBY
AlexSamBY merged commit 64783e9 into master Oct 1, 2026
5 checks passed
@AlexSamBY
AlexSamBY deleted the ts-e5 branch October 1, 2026 12:28
AlexSamBY added a commit that referenced this pull request Oct 1, 2026
## What changes

**This finishes the TypeScript migration.** It meets every clause of the
definition of done in `docs/UPGRADE-PLAN.md` §9.6:
- `src/core` + `src/library` are TypeScript under `strict`. That was E2
and E3, the last of it in #144.
- `allowJs` is off. That is this PR.
- The declarations are generated from the source. That was E4, #146.
- `propTypes` and `prop-types` are gone. That was E5, #145.

The demo and the test suites stay JavaScript, as the definition of done
allows.

### `allowJs` is off

- **The typecheck program is the TypeScript and nothing else:** 418
files before, 137 now. The 281 that left were 234 `.js`, 9 `.jsx` and 36
`.json` files that tsc resolved but never checked, plus the two guard
files deleted below.
- **A `.ts` file that imports a `.js` module now fails** with TS7016. I
measured this with a probe file, then deleted it. During the migration
this was `allowJs: true` with `checkJs: false`, so that a converted file
could import an unconverted one.
- **The typecheck takes 1.56 s, down from 1.80 s**, the median of three
runs each.
- `tsconfig.build.json` loses its own `allowJs: false`, which it now
inherits.

### The E0 guard is deleted

`src/toolchain/` was a two-file fixture. It proved that the checker sees
`.ts` and that Babel strips it. Its header asked for its deletion "once
real converted modules cover the same ground", and they do: the
product's TypeScript is built by all three pipelines and imported by
every suite. No `.test.ts` is left, because the tests are JavaScript.

### Three casts that still gave the migration as their reason

**`PopupContent` re-typed the renderer because it was "still
JavaScript".** I removed the cast, and the checker found what it had
been hiding:
- the popup hands `Render` a `null` `relativePath` and `relativeIndex`;
- `Render` passes them on to its children, because it tests `!==
undefined`;
- `Render`'s prop types said neither could be `null`.

They now allow `null`, and the cast is gone. No behaviour changed.

**`Data` and `ToggleField` called their casts `UnconvertedComponent`.**
Both still need a cast, for reasons of their own:
- a registry slot that `utils` types `unknown`, because it sits below
the layer that fills it;
- a nested document's `initialValues`, which the form layer types as a
record and `Data` as `unknown`.

The type is now `OpenComponent`, and the comments give those reasons.
`Data`'s import is renamed from `UIRenderWithUISetupJs` to
`DocumentWithOwnForm`. Its `as unknown as` cast was double; I checked
that a single `as` suffices.

### Docs

- `CLAUDE.md` says the migration is complete, and that a `.ts` file
cannot import JavaScript.
- `docs/UPGRADE-PLAN.md` marks the definition of done as met and records
the guard's deletion.
- The CI comment on the Typecheck step no longer describes unconverted
JavaScript.

## Measured

| | master | this PR |
|---|---|---|
| typecheck program | 418 files | 137 files |
| `npm run typecheck`, median of 3 | 1.80 s | 1.56 s |
| corpus, 38 examples: commits at mount / `translate` calls | 107 / 1012
| 107 / 1012 |
| on an edit: commits / `translate` calls | 27 / 64 | 27 / 64 |
| DOM after mount and after an edit | | identical, all 38 |
| `dist/index.js` | 311,539 bytes | 311,535 bytes |

The 4 bytes are `PopupContent`'s alias. Both bundles were built from
`git archive` copies in directories of the same name length.

## Gates

All exited 0, run on this branch:
- `lint:js`, `lint:css`, `typecheck`, `typecheck:contract`;
- `docs:props:check`, `docs:views:check`, `css:fixture:check`;
- `test:env-flags`, `build`;
- `test:coverage`, thresholds included;
- `test:pack`, `test:pack:peers`, `test:types`;
- `test:react16`, `test:react17`, `test:react19`, 2855 tests each, with
no worker crash;
- `test:e2e`, 41 tests;
- the manifest contract.

The suite went from 2857 to 2855 tests: the two were the deleted guard's
own.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

1 participant