Quick answer
Your object doesn't have every property the target type requires. The compiler lists exactly which ones. Pick the fix that matches the truth:
- They belong there — add the missing properties.
- They're genuinely optional — mark them
?in the interface. - You only ever have a subset — type it
Partial<T>orPick<T, ...>. - Wrong object — you're passing something that was never meant to satisfy this type.
Don't reach for as — it hides the gap without filling it.
The exact error string
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
const u: User = { id: 1, name: "Ada" };
// error TS2739: Type '{ id: number; name: string; }' is missing the following
// properties from type 'User': email, createdAt
// With exactly ONE property missing you get the singular sibling instead:
//
// error TS2741: Property 'createdAt' is missing in type
// '{ id: number; name: string; email: string; }' but required in type 'User'.
TypeScript compared two shapes and yours came up short. This is structural typing at work: the compiler doesn't care what your value is called or where it came from — only whether it carries every required property, at a compatible type. The message is unusually cooperative here, because it names the exact missing fields; the work is deciding which of the four fixes below reflects reality.
TS2739 vs TS2741 — the same check, counted differently
These two codes confuse people into thinking they're different problems. They aren't — it's one structural check, and which code you see generally tracks how many properties are missing:
| Code | Typically reported when | Wording |
|---|---|---|
| TS2741 | A single required property is missing | "Property 'x' is missing in type A but required in type B" |
| TS2739 (this page) | Multiple required properties are missing | "missing the following properties from type 'Y': a, b" |
Treat that as the usual pattern rather than a guaranteed rule — which diagnostic the compiler picks isn't a contracted behaviour, and it can differ by TypeScript version and by the assignability context the check runs in. It doesn't matter much in practice, because the cause and the fixes are the same either way.
Same cause, same fixes — only the count and the phrasing change. If you fix one missing field out of three, TS2739 remains while two are still missing. Once only one required property remains, the diagnostic changes to TS2741. That progression is normal and not a sign you've made things worse.
The same error in other positions
Every example here assigns to a typed const, but that's just the tidiest way to show it. The check runs anywhere a value flows into a typed slot, so a function return and an array element fail identically:
// Function return — checked against the declared return type
function makeUser(): User {
return { id: 1, name: "Ada" }; // ❌ TS2739: missing email, createdAt
}
// Array element — checked against the element type
const users: User[] = [{ id: 1, name: "Ada" }]; // ❌ same error
// Also: function arguments, though those usually surface as TS2345
Same cause, same four fixes below — only the position differs. Worth recognising because the error text names the shapes rather than the location, so a return-position failure reads exactly like the assignment ones.
Fix 1: supply the missing properties
The default answer, and the right one whenever the fields genuinely belong on this object:
// ❌ const u: User = { id: 1, name: "Ada" };
// ✅ every required property present
const u: User = {
id: 1,
name: "Ada",
email: "ada@example.com",
createdAt: new Date(),
};
If the values genuinely aren't available at this point in the code, that's information — it usually means the type is wrong for this stage of the data's life, which is Fix 3.
Fix 2: mark the properties optional
If a field is legitimately not always present, say so in the type with ?:
interface User {
id: number;
name: string;
email?: string; // may be absent
createdAt?: Date;
}
const u: User = { id: 1, name: "Ada" }; // ✅ compiles
Understand the trade before reaching for this: email?: string means the property's type is now string | undefined everywhere it is read, so every consumer needs a guard — and skipping that guard is how you end up at TS18048: 'x' is possibly 'undefined'. Marking a field optional purely to silence this error moves the problem downstream and multiplies it across every call site.
The honest test: would a reviewer agree this field is sometimes legitimately absent in your domain? If yes, optional is correct. If it's really "required, but I don't have it yet", use Fix 3.
Fix 3: model a partial object with Partial<T> or Pick<T, ...>
This is the fix for the extremely common case of building an object up in stages, or accepting a subset for an update:
// Building up in stages — every field optional while incomplete
const draft: Partial<User> = {};
draft.id = 1;
draft.name = "Ada";
// ... later, once complete:
const u: User = { ...draft, email: "ada@example.com", createdAt: new Date() };
// A function that only needs some fields — say exactly which
function label(user: Pick<User, "id" | "name">) {
return `#${user.id} ${user.name}`;
}
label({ id: 1, name: "Ada" }); // ✅ no email/createdAt required
// A patch payload — all fields optional except the identifier
type UserPatch = Partial<User> & { id: number };
Pick is the sharper tool of the two: it states positively which fields the code needs, so a reader knows the contract without checking the implementation, and adding a required field to User later won't break the function. Partial<T> makes everything optional, which is right for drafts and patches but too loose as a general-purpose escape from this error.
Fix 4: you're passing the wrong object
Sometimes the error is correct and the fix is neither the type nor the object literal — you're simply handing over the wrong value. Two shapes of this are worth recognising:
// A response envelope, where the payload is one level down
const res = await fetch("/api/user");
const body = await res.json(); // any
// ❌ the envelope isn't a User — it CONTAINS one
// const u: User = body;
// ✅
const u: User = body.data;
// A DTO that only resembles the domain type
interface UserRow { id: number; full_name: string; } // snake_case from SQL
// ❌ const u: User = row; → missing name, email, createdAt
// ✅ map it explicitly
const u: User = {
id: row.id,
name: row.full_name,
email: row.email,
createdAt: new Date(row.created_at),
};
The API-envelope version is the one that catches people most often, and it's worth pausing on: res.json() returns any, so nothing is checked until you assign it to a typed variable — which is exactly where TS2739 finally surfaces the mismatch. That late detection is a feature; the alternative is discovering it as Cannot read properties of undefined at run time. If you're shaping TypeScript interfaces from real API responses, our JSON to TypeScript converter generates the interface from a sample payload so the shape matches what the server actually sends.
Why as is the wrong tool
It's tempting, it compiles, and it's the one fix that makes things worse:
// ❌ compiles, then explodes at run time
// const u = { id: 1, name: "Ada" } as User;
// u.email.toLowerCase(); → TypeError: Cannot read properties of undefined
// ✅ if you truly must start incomplete, keep the gap visible
const draft: Partial<User> = { id: 1, name: "Ada" };
An assertion tells the compiler to stop checking; it does not create the properties. u.email is still undefined at run time, so you've converted a compile-time error you could see into a runtime crash you can't. Reserve as for cases where you genuinely know more than the compiler — and a missing property is the opposite of that.
Related assignability errors
| Code | What failed | Typical fix |
|---|---|---|
| TS2739 / TS2741 (this page) | Required properties are absent | Add them, mark optional, or use Partial/Pick |
| TS2322 | Properties present but a type mismatches | Fix the value's type, or widen the target |
| TS2345 | The same mismatch, on a function argument | Fix the argument or the parameter type |
| TS2339 | Reading a property the type doesn't declare — the inverse | Add it to the type, or narrow first |
Useful shortcut when triaging: TS2739 is about what's absent from a value you're supplying, while TS2339 is about what's absent from a type you're reading. Same structural engine, opposite directions.
Debugging checklist
- ✓ Read the listed property names — the compiler already told you exactly what's missing
- ✓ Do those fields belong on this object? Then add them (Fix 1)
- ✓ Are they legitimately sometimes-absent? Mark them
?— and expect| undefinedat every read site - ✓ Building the object in stages? Use
Partial<T>while incomplete - ✓ Function only needs some fields? Declare
Pick<T, "a" | "b">instead of the whole type - ✓ Coming from an API? Check whether the payload is nested (
body.data, notbody) - ✓ Started after a dependency upgrade? A library type may have gained required fields
- ✓ Resist
as— it converts a compile error into a runtimeundefined - ✓ Only one field missing? That's TS2741 — same page, same fixes
Frequently Asked Questions
What does TS2739 mean?
TypeScript compared the shape of the value you supplied against the shape the target type requires, and yours is missing at least two required properties — the message names them. It is a structural check: TypeScript does not care what the value is called or where it came from, only whether it has every required property with a compatible type.
What is the difference between TS2739 and TS2741?
They are the same structural check, reported differently depending on how much is missing. TS2741 is commonly used when a single required property is missing and reads "Property 'x' is missing in type A but required in type B". TS2739 is typically reported when multiple properties are missing and lists them together as "missing the following properties". Which code you get is not a contracted rule and can vary by TypeScript version, but it rarely matters: the cause and the fixes are identical either way.
How do I make properties optional so the error goes away?
Add a question mark to the property in the interface — name?: string — which makes it legal to omit but also makes its type string | undefined everywhere it is read. Do this only when the field is genuinely optional in your domain. If it is required but simply not available yet at this point in the code, making it optional pushes undefined checks onto every consumer rather than fixing the real gap.
Should I use 'as' to silence TS2739?
No. An as assertion tells the compiler to stop checking, but the properties are still absent at run time, so the first read of a missing field yields undefined and usually crashes downstream. The error is reporting a real gap between the data you have and the data the type promises. Use Partial, Pick, or a correct object instead, and reserve as for cases where you genuinely know more than the compiler.
Why do I get TS2739 when assigning an empty object?
An empty object literal has none of the required properties, so every one of them is reported at once — this is the most common way to trigger TS2739 rather than its single-property sibling TS2741. If you need to start empty and fill the object in later, type the variable as Partial<T> while building it and convert to T once it is complete, so the compiler tracks the gap instead of ignoring it.
Why does TS2739 mention properties I did not think were required?
Every property in an interface is required unless it carries a question mark, so fields you think of as having defaults are still mandatory to the type system unless declared optional. This also surfaces when a library updates and adds required fields to a type you were already constructing — the object did not change, but the contract it must satisfy did.
References
- Object Types (TypeScript Handbook)
- Type Compatibility (TypeScript Handbook)
Generate the interface from real JSON
If you're hand-writing types for an API payload, paste a sample response and get the interface — shapes match what the server actually sends.