- Регистрация
- 9 Май 2015
- Сообщения
- 1,559
- Баллы
- 155
'PropsWithChildren' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.ts(1484)
If you’ve recently upgraded to TypeScript 5+ and started seeing this new error in your React project:
'PropsWithChildren' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.ts(1484)
Don’t worry — you didn’t break React.
You just met one of the most important compiler upgrades in recent TypeScript history: verbatimModuleSyntax.
The Problem Example
Let’s say you’re using TanStack Query v5 and write a helper like this:
import { PropsWithChildren } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
export function withQueryClient(ui: React.ReactElement) {
const client = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
const Wrapper = ({ children }: PropsWithChildren) => (
<QueryClientProvider client={client}>{children}</QueryClientProvider>
);
return { ui, Wrapper };
}
TypeScript 5.0+ throws the error because you’re importing a type (PropsWithChildren) as if it were a runtime value — and you have "verbatimModuleSyntax": true in your tsconfig.json.
Why This Happens
Historically, TypeScript performed import elision — it analyzed which imports were only used for types, and dropped them automatically during compilation.
But this caused headaches when mixing type imports, side-effect imports, and export elision.
So TypeScript 5 introduced a simpler and more explicit rule set:
Example:Under verbatimModuleSyntax, TypeScript no longer elides imports automatically.
You must explicitly mark type-only imports using the type modifier.
// Old way (TS < 5.0)
import { PropsWithChildren } from 'react';
// New way (TS 5+ with verbatimModuleSyntax)
import type { PropsWithChildren } from 'react';
Now, what you see in your import statements is exactly what you’ll get in the compiled JavaScript.
Fixing the Error (3 Proven Options)
 Option 1 — Use a Type-Only Import (Recommended)
 Option 1 — Use a Type-Only Import (Recommended)import type { PropsWithChildren } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
export function withQueryClient(ui: React.ReactElement) {
const client = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
const Wrapper = ({ children }: PropsWithChildren) => (
<QueryClientProvider client={client}>{children}</QueryClientProvider>
);
return { ui, Wrapper };
}
? This is the correct, future-proof way. It satisfies the compiler and makes your imports explicit.
 Option 2 — Use the React Namespace
 Option 2 — Use the React Namespaceimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';
export function withQueryClient(ui: React.ReactElement) {
const client = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
const Wrapper = ({ children }: React.PropsWithChildren }) => (
<QueryClientProvider client={client}>{children}</QueryClientProvider>
);
return { ui, Wrapper };
}
 React.PropsWithChildren references the global React types from @types/react, so you don’t need to import anything at all.
 React.PropsWithChildren references the global React types from @types/react, so you don’t need to import anything at all. Option 3 — Disable verbatimModuleSyntax (Not Recommended)
 Option 3 — Disable verbatimModuleSyntax (Not Recommended)// tsconfig.json
{
"compilerOptions": {
"verbatimModuleSyntax": false
}
}
This brings back TypeScript’s older “import elision” behavior — but removes the clarity and consistency gained in 5.0+.
Use this only as a temporary migration step.
Bonus: Improving the Test Wrapper Pattern
If your wrapper is for testing (e.g., Vitest, Jest), you can enhance it a bit:
import type { PropsWithChildren } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
export function withQueryClient(ui: React.ReactElement) {
const client = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
function Wrapper({ children }: PropsWithChildren) {
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
}
return { ui, Wrapper, client };
}
Then in your tests:
import { render } from '@testing-library/react';
import { withQueryClient } from './withQueryClient';
test('renders data', () => {
const { ui, Wrapper } = withQueryClient(<MyComponent />);
render(ui, { wrapper: Wrapper });
});
Deep Dive — What verbatimModuleSyntax Actually Does
Before TypeScript 5.0, the compiler tried to “guess” which imports were needed at runtime.
This guessing game led to subtle inconsistencies across compilers, bundlers, and file types.
Example (before TypeScript 5.0)
import { Car } from './car';
export function drive(car: Car) {
// ...
}
After transpilation, TypeScript elides the import entirely:
export function drive(car) {
// ...
}
But this behavior was inconsistent — what if ./car had side effects?
What if another compiler (like Babel) didn’t drop it the same way?
The Fix: Simplicity via Explicitness
verbatimModuleSyntax means:
- Imports without type → stay in emitted JS.
- Imports with type → erased entirely.
That makes builds predictable and aligns TypeScript with the ECMAScript standard.“What you see is what you get.”
Related Deprecations
- --importsNotUsedAsValues → Deprecated.
- --preserveValueImports → Deprecated.
- --verbatimModuleSyntax → The new standard for clarity and correctness.
| Scenario | Problem | Fix | 
|---|---|---|
| Importing only types as values | 'X' is a type and must be imported using a type-only import | Use import type { X } | 
| Using both values & types from the same module | Compiler warns you | Combine both: import { Button, type ButtonProps } from './Button' | 
| Tooling mismatch | Old Babel/TS config | Ensure your bundler supports modern ESM and TS 5+ | 
verbatimModuleSyntax is one of those rare TypeScript changes that simplifies your mental model instead of complicating it.
| Old World | New World | 
|---|---|
| Implicit import guessing | Explicit type/value imports | 
| Complex flags (importsNotUsedAsValues) | One clear rule | 
| Risk of side-effect elision | Deterministic output | 
In short:
With this understanding, the 'PropsWithChildren' error is no longer a mystery — it’s a gentle nudge from TypeScript guiding you toward better module hygiene.Use import type for types.
Use import for values.
? Be explicit, not implicit.
 Written by Cristian Sifuentes — Full‑stack developer & AI/JS enthusiast focused on resilient frontends, scalable architectures, and TypeScript excellence.
 Written by Cristian Sifuentes — Full‑stack developer & AI/JS enthusiast focused on resilient frontends, scalable architectures, and TypeScript excellence.Tags: #react #typescript #frontend #architecture #programming
Источник:
 
				