With exactOptionalPropertyTypes on, an optional property (nickname?: string) may be missing, but it may not be present with the value undefined. TS2375 reports an object that has it anyway, in a typed variable; TS2379 reports the same in an argument, and TS2412 in an assignment to the property.
The difference matters to code that checks "nickname" in profile or iterates over keys: a property set to undefined is there, a missing one is not. Without the option, TypeScript treats the two as the same.
At a glance
| Code | TS2375 |
|---|---|
| Message | Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. |
Also on this page: TS2379 | Argument of type '{0}' is not assignable to parameter of type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. |
Also on this page: TS2412 | Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target. |
| TypeScript's category | error |
| Listed under | Null, undefined and unknown |
| Turned on by | exactOptionalPropertyTypes |
| In .js files | Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file |
The messages are TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} and {1} are filled in by tsc for each error.
What it looks like
/** @typedef {{ name: string, nickname?: string }} Profile */
/** @param {Profile} profile */
function save(profile) {
return profile.name;
}
/** @param {string | undefined} nickname */
export function create(nickname) {
/** @type {Profile} */
const profile = { name: "Ada", nickname };error TS2375: Type '{ name: string; nickname: string | undefined; }' is not assignable to type 'Profile' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'nickname' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.
save({ name: "Ada", nickname });error TS2379: Argument of type '{ name: string; nickname: string | undefined; }' is not assignable to parameter of type 'Profile' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'nickname' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.
profile.nickname = undefined;error TS2412: Type 'undefined' is not assignable to type 'string' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.
return profile;
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": [],
"exactOptionalPropertyTypes": true
},
"include": ["**/*.js"]
} What causes it
- An object literal copies a value that may be
undefinedinto an optional property ({ name, nickname }). - Code clears an optional property by assigning
undefinedinstead of deleting it.
How to fix it
Leave the property out when there is no value
Build the object without the property when the value is missing, and remove a property with delete rather than assigning undefined.
/** @typedef {{ name: string, nickname?: string }} Profile */
/** @param {string | undefined} nickname */
export function create(nickname) {
/** @type {Profile} */
const profile = nickname === undefined ? { name: "Ada" } : { name: "Ada", nickname };
delete profile.nickname;
return profile;
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": [],
"exactOptionalPropertyTypes": true
},
"include": ["**/*.js"]
} Or allow undefined in the type
When undefined is a meaningful value for the property, say so: nickname?: string | undefined. Then both forms are accepted, as without the option.
/** @typedef {{ name: string, nickname?: string | undefined }} Profile */
/** @param {string | undefined} nickname */
export function create(nickname) {
/** @type {Profile} */
const profile = { name: "Ada", nickname };
profile.nickname = undefined;
return profile;
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": [],
"exactOptionalPropertyTypes": true
},
"include": ["**/*.js"]
} Gotchas
exactOptionalPropertyTypesneedsstrictNullChecks(part ofstrict), and it is not part ofstrictitself; the strict level of the jsconfig generator turns it on.- Library types that declare optional properties without
| undefinedreject anundefinedvalue under this option, so passing a library an options object built from values that may be missing is a common source of TS2379.
Sources
- TSConfig Reference: exactOptionalPropertyTypes · TypeScript
- diagnosticMessages.json (TypeScript 5.9.3) · TypeScript on GitHub
Last reviewed 21 October 2026.