Skip to content

Commit 24a8fb3

Browse files
STRMLjoshkel
andcommitted
Merge PR #809: fix partial/undefined props under React 18 TypeScript
Fixes #807. Making the propTypes static optional keeps it out of the required public type surface. React <= 18's JSX LibraryManagedAttributes only consults propTypes when it is a required member; when it does, the index-signature propTypes cancels defaultProps optionality and consumers must pass every prop. React 19 ignores propTypes entirely, so v19 alone could not catch this. Adds typings/tsconfig.react18.json, wired into make lint, which type-checks the public surface against @types/react@18 so the regression cannot return. The PR's yarn.lock was dropped and regenerated on top of master: it had been rebuilt against a different registry and lost all 49 platform-optional esbuild/rollup binaries, which would break yarn install --frozen-lockfile on Linux CI. Co-authored-by: Josh Kelley <joshkel@gmail.com>
2 parents 980374b + 6b587ca commit 24a8fb3

6 files changed

Lines changed: 60 additions & 11 deletions

File tree

‎Makefile‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ lint:
1515
@$(BIN)/eslint lib
1616
@$(BIN)/tsc --noEmit
1717
@$(BIN)/tsc -p typings
18+
@$(BIN)/tsc -p typings/tsconfig.react18.json
1819

1920
# tsup emits cjs + esm + dts into build/cjs (and rewrites build/cjs/cjs.js to the
2021
# legacy module.exports === Draggable shape). webpack emits the UMD global bundle.

‎lib/Draggable.tsx‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,20 @@ class Draggable extends React.Component<Partial<DraggableProps>, DraggableState>
5151

5252
static displayName?: string = 'Draggable';
5353

54-
// The index-signature annotation is load-bearing: without it tsc infers the
55-
// PropTypes.Requireable<...> types and emits `import PropTypes from 'prop-types'`
56-
// into the generated public .d.ts, forcing consumers to install
57-
// @types/prop-types (the v4.5.0 hand-written typings had no prop-types dep).
54+
// Both the annotation and the `?` are load-bearing:
55+
// - The index-signature annotation stops tsc from inferring the
56+
// PropTypes.Requireable<...> types and emitting `import PropTypes from
57+
// 'prop-types'` into the generated public .d.ts, which would force consumers
58+
// to install @types/prop-types (the v4.5.0 hand-written typings had none).
59+
// - The `?` keeps `propTypes` from being a *required* member of the public
60+
// type. React <= 18's JSX LibraryManagedAttributes only consults a
61+
// component's `propTypes` when it is required (`C extends {propTypes: ...}`);
62+
// when it does, this index-signature `propTypes` makes `defaultProps` stop
63+
// marking props optional, so consumers are forced to pass every prop.
64+
// Optional dodges that branch; React 19 ignores `propTypes` entirely. The
65+
// typings/tsconfig.react18.json check guards against a regression here.
5866
// Do not remove. See lib/DraggableCore.tsx for the same guard.
59-
static propTypes: {[key: string]: unknown} = {
67+
static propTypes?: {[key: string]: unknown} = {
6068
// Accepts all props <DraggableCore> accepts.
6169
...DraggableCore.propTypes,
6270

‎lib/DraggableCore.tsx‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,20 @@ export default class DraggableCore extends React.Component<Partial<DraggableCore
7474

7575
static displayName: string | undefined = 'DraggableCore';
7676

77-
// The index-signature annotation is load-bearing: without it tsc infers the
78-
// PropTypes.Requireable<...> types and emits `import PropTypes from 'prop-types'`
79-
// into the generated public .d.ts, forcing consumers to install
80-
// @types/prop-types (the v4.5.0 hand-written typings had no prop-types dep).
77+
// Both the annotation and the `?` are load-bearing:
78+
// - The index-signature annotation stops tsc from inferring the
79+
// PropTypes.Requireable<...> types and emitting `import PropTypes from
80+
// 'prop-types'` into the generated public .d.ts, which would force consumers
81+
// to install @types/prop-types (the v4.5.0 hand-written typings had none).
82+
// - The `?` keeps `propTypes` from being a *required* member of the public
83+
// type. React <= 18's JSX LibraryManagedAttributes only consults a
84+
// component's `propTypes` when it is required (`C extends {propTypes: ...}`);
85+
// when it does, this index-signature `propTypes` makes `defaultProps` stop
86+
// marking props optional, so consumers are forced to pass every prop.
87+
// Optional dodges that branch; React 19 ignores `propTypes` entirely. The
88+
// typings/tsconfig.react18.json check guards against a regression here.
8189
// Do not remove. See lib/Draggable.tsx for the same guard.
82-
static propTypes: {[key: string]: unknown} = {
90+
static propTypes?: {[key: string]: unknown} = {
8391
/**
8492
* `allowAnyClick` allows dragging using any mouse button.
8593
* By default, we only accept the left button.

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"@types/node": "^25.0.3",
5858
"@types/prop-types": "^15.7.15",
5959
"@types/react": "^19.2.7",
60+
"@types/react-18": "npm:@types/react@^18.3.27",
6061
"@types/react-dom": "^19.2.3",
6162
"@typescript-eslint/eslint-plugin": "^8.18.0",
6263
"@typescript-eslint/parser": "^8.18.0",

‎typings/tsconfig.react18.json‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"//": "Type-checks the public API against @types/react@18 as well as the default v19. React 18's JSX `LibraryManagedAttributes` consults a component's `propTypes`, unlike v19 which ignores it. A `propTypes` member leaking into the public types there makes `defaultProps` stop marking props optional, so React 18 consumers must pass every prop. v19 alone cannot catch that regression. Only `react` is redirected to the v18 types; `react-dom` stays on its installed v19 types (skipLibCheck ignores any v18/v19 mismatch inside its .d.ts). Aliasing react-dom too would pull in an `@types/react-dom-18` whose `@types/react@^18` peer cannot be satisfied under npm's strict resolution.",
3+
"compilerOptions": {
4+
"noEmit": true,
5+
"jsx": "preserve",
6+
"strict": true,
7+
"target": "ES2019",
8+
"lib": ["ES2019", "DOM", "DOM.Iterable"],
9+
"moduleResolution": "node",
10+
"esModuleInterop": true,
11+
"baseUrl": ".",
12+
"types": ["node"],
13+
"skipLibCheck": true,
14+
"paths": {
15+
"react-draggable": ["../lib/cjs.ts"],
16+
"react": ["../node_modules/@types/react-18"],
17+
"react/jsx-runtime": ["../node_modules/@types/react-18/jsx-runtime"]
18+
}
19+
},
20+
"files": [
21+
"test.tsx"
22+
]
23+
}

‎yarn.lock‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1407,7 +1407,7 @@
14071407
dependencies:
14081408
undici-types ">=7.24.0 <7.24.7"
14091409

1410-
"@types/prop-types@^15.7.15":
1410+
"@types/prop-types@*", "@types/prop-types@^15.7.15":
14111411
version "15.7.15"
14121412
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7"
14131413
integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==
@@ -1422,6 +1422,14 @@
14221422
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
14231423
integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
14241424

1425+
"@types/react-18@npm:@types/react@^18.3.27":
1426+
version "18.3.31"
1427+
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.31.tgz#b5e95e28ffcceab8d982f33f2eb076e17653c2a4"
1428+
integrity sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==
1429+
dependencies:
1430+
"@types/prop-types" "*"
1431+
csstype "^3.2.2"
1432+
14251433
"@types/react-dom@^19.2.3":
14261434
version "19.2.3"
14271435
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c"

0 commit comments

Comments
 (0)