Partial and Required on a Form Draft Object
Derive a draft type with Partial, require every field with Required, and write one check that turns a draft into the finished type. After this, an unfinished form cannot reach the submit function.
A form that autosaves has two states of the same data. While the person is typing, any field may be missing. When they press submit, every field has to be there. If you write one type and make every field optional, the submit function has to check everything again. If you write two types by hand, they drift apart. Partial and Required give you both from one source.
Step 1: one type for the finished thing
type Profile = {
name: string;
email: string;
age: number;
};
type ProfileDraft = Partial<Profile>;
const draft: ProfileDraft = { name: "Ana" };
console.log(draft);
Profile describes a complete profile and ProfileDraft is the same thing with every field optional. When you add phone: string to Profile, the draft type gets phone? without any edit.
Step 2: make the submit function demand all of it
type Profile = { name: string; email: string; age: number };
type ProfileDraft = Partial<Profile>;
function submit(profile: Profile): void {
console.log("saving", profile.name, profile.email, profile.age);
}
const draft: ProfileDraft = { name: "Ana" };
submit(draft);
submit.ts(9,8): error TS2345: Argument of type 'Partial<Profile>' is not assignable to parameter of type 'Profile'.
Types of property 'name' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.
The compiler refuses to send an unfinished draft. The message is indented, and you read it from the bottom: undefined is not a string, so the name property is not safe. It stops at the first property it finds. It does not list every missing field, so do not use it as the list you show the user.
Step 3: turn a draft into a profile with a check
A function that returns draft is Profile is a type predicate. It tells the compiler "if I return true, this is the full thing".
type Profile = { name: string; email: string; age: number };
type ProfileDraft = Partial<Profile>;
function isComplete(draft: ProfileDraft): draft is Profile {
return (
draft.name !== undefined &&
draft.email !== undefined &&
draft.age !== undefined
);
}
function submit(profile: Profile): void {
console.log("saving", profile.name, profile.email, profile.age);
}
const drafts: ProfileDraft[] = [
{ name: "Ana" },
{ name: "Ben", email: "ben@example.com", age: 31 },
];
for (const draft of drafts) {
if (isComplete(draft)) {
submit(draft);
} else {
console.log("still missing something");
}
}
still missing something
saving Ben ben@example.com 31
Inside the if, draft is a Profile, so submit(draft) compiles. After the if, it is still a draft. The predicate is a promise you make by hand, so keep the checks in it honest: every field of Profile has to be tested.
Step 4: where Required fits
Required<T> is the reverse of Partial<T>. It removes every question mark.
type Settings = { theme?: string; pageSize?: number };
const defaults: Required<Settings> = { theme: "light" };
required.ts(3,7): error TS2741: Property 'pageSize' is missing in type '{ theme: string; }' but required in type 'Required<Settings>'.
Use it for the object you build after you have filled in every default. The values from the user are Settings, the merged result is Required<Settings>, and the rest of the code never has to check for undefined.
⚠ Warning: Partial<Profile> also accepts an empty object, and it accepts a field set to undefined. If { name: undefined } should not count as filled in, test for undefined, which isComplete above does.
Common mistakes
- Writing the draft type by hand as a second
type. It will be out of date within a month. - Passing a
Partialstraight into a function that needs the full type and silencing the error withas Profile. - Checking only some fields in the predicate, so a "complete" draft is still missing one.
Related course: TypeScript Intermediate.
About
Derive a draft type with Partial, require every field with Required, and write one check that turns a draft into the finished type. After this, an unfinished form cannot reach the submit function.
- Responsible
- Mary Q
- Last Update
- 10/15/2026
- Reading Time
- 4 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.