React useState null vs undefined TS2322 fix in strict mode
TypeScript strict mode won't let you pass null to useState when the initial value is undefined. The fix is to set a proper initial value or widen the type.
Quick answer
Change your initial state to null or widen the type to string | null explicitly. The root cause: TypeScript's strict mode treats null and undefined as distinct types, and useState infers the type from the initial value.
What's actually happening here
You wrote something like:
const [value, setValue] = useState<string | undefined>(undefined);
// Later:
setValue(null); // TS2322: Argument of type 'null' is not assignable to parameter of type 'string | undefined'The error pops up in React 18 with TypeScript 4.4+ and strict mode enabled. The reason step 3 works is that TypeScript infers the type of useState from its initial value. When you pass undefined, the inferred type becomes string | undefined. But null is not assignable to undefined — they're sibling types under the null and undefined types, not interchangeable.
This catches real bugs. Say you're building a dropdown that's initially unset (undefined) but later you want to clear it to null to distinguish "user hasn't touched it yet" from "user explicitly cleared it." TypeScript forces you to be explicit about which one you mean.
The error appears in real scenarios like: a search input that starts undefined but you want to reset it to null after a clear button click, or a user profile field that's undefined before loading but null after a failed fetch.
Fix steps
- Check your TypeScript config — open
tsconfig.jsonand confirmstrict: trueorstrictNullChecks: trueis set. This is the default in modern Create React App and Vite setups. - Identify the problematic
useState— look for a call where the initial value isundefinedbut you're later settingnull. Example:const [name, setName] = useState<string | undefined>(undefined). - Change the initial value to
null— this is the simplest fix if you don't care about theundefinedvsnulldistinction.const [name, setName] = useState<string | null>(null). Now bothnullandstringare valid, and you can passnullfreely. - Or widen the type union — if you need both
nullandundefinedfor semantic reasons, change the type tostring | null | undefined:const [name, setName] = useState<string | null | undefined>(undefined). Now you can pass eithernullorundefinedtosetName. - If you're migrating from non-strict code, you might need to update multiple calls. Search your codebase for
useState.*undefinedand check if anysetStatecalls passnull. Fix all of them.
Alternative fixes if the main one fails
If you can't modify the initial value (say it comes from a library hook that returns undefined), use a type assertion:
setValue(null as string | null | undefined);But I'd avoid that — it bypasses type checking and can hide real issues. A better escape hatch is to cast through unknown:
setValue((null as unknown) as string | undefined);Even that's ugly. The real fix is to align your types properly.
If you're using useState with a complex object and you initialize it with undefined but later want null to mean "reset to empty," consider using a discriminated union or a separate boolean flag instead of mixing null and undefined.
Prevention tip
Stop treating null and undefined as the same thing. They're not. Pick one convention per project — I prefer null for "explicitly empty" and undefined for "not yet initialized." Then your types will be clean:
// If the value hasn't loaded yet:
const [user, setUser] = useState<User | undefined>(undefined);
// If the value can be empty after loading:
const [search, setSearch] = useState<string | null>(null);This way you never need to mix them. When you fetch data, initialize with undefined (loading state), and after fetch, use null to represent "no data." TypeScript strict mode will thank you.
One more thing: if you're using useEffect to reset state, make sure your cleanup or effect doesn't set null where the initial state was undefined. Same error shows up there too.
Was this solution helpful?