Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ lint:
@$(BIN)/eslint lib
@$(BIN)/tsc --noEmit
@$(BIN)/tsc -p typings
@$(BIN)/tsc -p typings/tsconfig.react18.json

# tsup emits cjs + esm + dts into build/cjs (and rewrites build/cjs/cjs.js to the
# legacy module.exports === Draggable shape). webpack emits the UMD global bundle.
Expand Down
18 changes: 13 additions & 5 deletions lib/Draggable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,20 @@ class Draggable extends React.Component<Partial<DraggableProps>, DraggableState>

static displayName?: string = 'Draggable';

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

Expand Down
18 changes: 13 additions & 5 deletions lib/DraggableCore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,20 @@ export default class DraggableCore extends React.Component<Partial<DraggableCore

static displayName: string | undefined = 'DraggableCore';

// The index-signature annotation is load-bearing: without it tsc infers the
// PropTypes.Requireable<...> types and emits `import PropTypes from 'prop-types'`
// into the generated public .d.ts, forcing consumers to install
// @types/prop-types (the v4.5.0 hand-written typings had no prop-types dep).
// Both the annotation and the `?` are load-bearing:
// - The index-signature annotation stops tsc from inferring the
// PropTypes.Requireable<...> types and emitting `import PropTypes from
// 'prop-types'` into the generated public .d.ts, which would force consumers
// to install @types/prop-types (the v4.5.0 hand-written typings had none).
// - The `?` keeps `propTypes` from being a *required* member of the public
// type. React <= 18's JSX LibraryManagedAttributes only consults a
// component's `propTypes` when it is required (`C extends {propTypes: ...}`);
// when it does, this index-signature `propTypes` makes `defaultProps` stop
// marking props optional, so consumers are forced to pass every prop.
// Optional dodges that branch; React 19 ignores `propTypes` entirely. The
// typings/tsconfig.react18.json check guards against a regression here.
// Do not remove. See lib/Draggable.tsx for the same guard.
static propTypes: {[key: string]: unknown} = {
static propTypes?: {[key: string]: unknown} = {
/**
* `allowAnyClick` allows dragging using any mouse button.
* By default, we only accept the left button.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@types/node": "^25.0.3",
"@types/prop-types": "^15.7.15",
"@types/react": "^19.2.7",
"@types/react-18": "npm:@types/react@^18.3.27",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.18.0",
"@typescript-eslint/parser": "^8.18.0",
Expand Down
23 changes: 23 additions & 0 deletions typings/tsconfig.react18.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"//": "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.",
"compilerOptions": {
"noEmit": true,
"jsx": "preserve",
"strict": true,
"target": "ES2019",
"lib": ["ES2019", "DOM", "DOM.Iterable"],
"moduleResolution": "node",
"esModuleInterop": true,
"baseUrl": ".",
"types": ["node"],
"skipLibCheck": true,
"paths": {
"react-draggable": ["../lib/cjs.ts"],
"react": ["../node_modules/@types/react-18"],
"react/jsx-runtime": ["../node_modules/@types/react-18/jsx-runtime"]
}
},
"files": [
"test.tsx"
]
}
Loading