"Type 'undefined' is not assignable" errors after enabling exactOptionalPropertyTypes on a shared library
0 reputation · 23 Jun 2026, 14:48 UTC
0 reputation · 23 Jun 2026, 14:48 UTC
After enabling exactOptionalPropertyTypes in a shared TypeScript library (assume TS 5.x), consumers that pass objects with explicitly-undefined optional properties now fail to compile with assignability errors such as Type 'undefined' is not assignable to type ... with 'exactOptionalPropertyTypes: true'. The flag is documented as opt-in and is not part of strict mode, so teams in the same monorepo compile the same interfaces under different rules.
The concrete goal is to decide on a consistent policy for optional properties across packages: some interfaces were written assuming prop?: T means "may be absent or undefined", while others intend "must be absent if not set". With the flag on, these two meanings diverge, and declaration files emitted from one package behave differently when consumed by another package with a different tsconfig.
Questions:
prop?: T be rewritten as prop?: T | undefined everywhere to stay compatible with both flag settings, or is it better to standardize the flag across all packages?strict mode that should influence the decision?prop?: T | undefined if you need the library to be usable by consumers that keep exactOptionalPropertyTypes disabled.{ prop: undefined } and the library will treat the property as absent, potentially breaking runtime assumptions.strict so that existing codebases can migrate gradually. It is an opt‑in feature introduced in TS 4.8 to give teams control over the stricter optional‑property semantics.exactOptionalPropertyTypes mattersWhen exactOptionalPropertyTypes is true, the compiler treats prop?: T as "property may be omitted, but if present it must be of type T". Explicit undefined is rejected. With the flag off, prop?: T is equivalent to prop?: T | undefined, allowing consumers to write { prop: undefined }.
Suppose the library declares
interface Foo { prop?: string; }
and is compiled with exactOptionalPropertyTypes enabled. A consumer compiled with the flag disabled can write:
const foo: Foo = { prop: undefined };
The library will treat prop as absent, but the runtime code may still expect a defined string. This mismatch is a classic example of unsoundness introduced by different compiler flags.
*.d.ts files.
exactOptionalPropertyTypes in every package and update consumers accordingly.
undefined, rewrite those properties as prop?: T | undefined.
tsconfig.json note explaining the chosen flag policy to avoid future drift.
In rare cases you may want to expose a type that accepts undefined while still keeping the stricter semantics for internal use. You can create a separate interface for the consumer API:
interface FooConsumer = Omit & { prop?: string | undefined };
Export FooConsumer for external consumers, and keep Foo for internal code.
Standardize exactOptionalPropertyTypes across the monorepo or explicitly widen optional types to ... | undefined if you must keep divergent settings. The flag is opt‑in to allow gradual migration; keeping it disabled in some packages while enabled in others introduces unsoundness at the boundary.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.