TS2322 Type is not assignable to type (tested)

error TS2322: Type 'X' is not assignable to type 'Y' is TypeScript's generic shape-mismatch error. It fires whenever a value's type doesn't line up with the type you declared, and the exact wording changes depending on whether a property is missing, null-ish, or just the wrong primitive. We reproduced four common triggers in a real TypeScript 7 project and recorded the exact text tsc prints for each.

1. The error is generic on purpose

TS2322 is TypeScript's catch-all for "this value doesn't fit that type." It says nothing by itself about why. The useful detail is almost always in a second line tsc appends, or in a different, more specific error code it emits instead (we saw this with a missing property below). Read the whole message, not just the first sentence.

2. Assigning null to a string, with and without strictNullChecks

Same two lines, two different results depending on one setting:

let s: string;
s = null;

With "strict": false (which also turns off strictNullChecks), npx tsc compiled this with no error at all — null is treated as assignable to every type. With "strict": true, the identical two lines gave:

s1.ts(2,1): error TS2322: Type 'null' is not assignable to type 'string'.

Fix: turn on strictNullChecks (part of strict) project-wide so this class of bug is caught, then either give the variable a real value, widen its type to string | null if null is a legitimate state, or use the non-null assertion (s!) only when you are certain it can't happen there.

3. Object literal missing a required property

We declared an interface with three required fields and assigned a literal with only two:

interface User {
  id: number;
  name: string;
  email: string;
}

const u: User = { id: 1, name: "Ada" };

tsc did not print plain TS2322 here. It printed a more specific code, TS2741:

s2.ts(7,7): error TS2741: Property 'email' is missing in type '{ id: number; name: string; }' but required in type 'User'.

TS2741 names the exact missing property, which is why it's worth knowing: searching only for "TS2322" would miss this message. Fix: add the missing property, or mark it optional in the interface (email?: string) if it's genuinely not always required.

4. Assigning undefined to a non-optional prop

A function parameter typed with a required string field, called with a value that's typed string | undefined:

interface Props {
  label: string;
}

function render(p: Props) {
  return p.label;
}

const value: string | undefined = undefined;
render({ label: value });

tsc printed plain TS2322 again, pointing at the property value inside the object literal:

s3.ts(10,10): error TS2322: Type 'undefined' is not assignable to type 'string'.

This is the same shape of bug you hit constantly with optional React/JSX props: a value that can be undefined (an optional prop, an unchecked Map.get, a destructured field) flowing into a slot typed as always-present. Fix: either make the field optional (label?: string) and handle undefined where you read it, or require the caller to prove a value exists first (a default, a guard, or narrowing before the call).

5. The escape hatch: as unknown as X

A double cast through unknown bypasses the assignability check entirely, on purpose. We cast an object missing a required field straight into the interface:

interface User {
  id: number;
  name: string;
}

const raw = { id: 1 };
const u = raw as unknown as User;
console.log(u.name.toUpperCase());

npx tsc compiled this with zero errors. as unknown as User tells the compiler "trust me," and it does — even though name doesn't exist at runtime, so u.name.toUpperCase() throws TypeError: Cannot read properties of undefined the moment it runs. We confirmed the throw by running the compiled output with Node. Use this only at real trust boundaries (e.g. you've validated the shape with a runtime check or schema library a line earlier), never as a quick way to silence TS2322 on code you haven't actually verified.

6. Quick checklist

Frequently asked questions

Is TS2322 always about the wrong primitive type?

No. In our tests the same general mismatch produced plain TS2322 for null/undefined and for primitive-to-primitive cases, but a missing required property on an object literal produced a different, more specific code, TS2741, instead.

Why did null compile with no error in one of my projects?

Check strictNullChecks. We measured the exact same `s: string; s = null;` compiling cleanly with strict:false and failing with TS2322 under strict:true. It's a project setting, not a language default.

Does as unknown as X actually check anything?

No. In our test, casting an object missing a required field straight to an interface via as unknown as X produced zero compile errors, and then threw a real TypeError at runtime when the missing property was used. The cast only silences the compiler.

What's the difference between TS2322 and TS2741?

TS2322 is the generic 'Type X is not assignable to type Y' message. TS2741 is more specific: it fires when an object literal is missing a property the target type requires, and names that property in the message.

How do I fix TS2322 for an optional prop that might be undefined?

In our test, passing a string | undefined value into a required string prop gave TS2322 pointing at the value. Either mark the field optional (label?: string) and handle undefined where you read it, or ensure a real value before the call site.

Written with AI assistance. Measured on 2026-10-02 with TypeScript 7.0.2 (npx tsc), Node.js 24.21.0 and npm 11.19.0, in a fresh project (npm init -y && npm i -D typescript). Each scenario is its own minimal .ts file compiled with a one-off tsconfig.json; all error text below is copy-pasted from real tsc runs. PlainHeaders is our extension; curl, Python, Node.js and Chromium are free.