Skip to content

With exactOptionalPropertyTypes enabled, assigning a union with missing literal properties is incorrectly allowed #61678

Open
@loganzartman

Description

@loganzartman

🔎 Search Terms

"exactoptionalpropertytypes union", "function call union assignability", "discriminated union assignment allowed incorrectly", "discriminated union assignable missing property"

🕗 Version & Regression Information

  • This changed between versions 4.3.5 and 4.4.4
  • still exists in nightly 5.9.0

⏯ Playground Link

https://www.typescriptlang.org/play/?exactOptionalPropertyTypes=true&ts=5.9.0-dev.20250508#code/KYDwDg9gTgLgBDAnmYcCqcC8cCQBYAKBwB84BvJFALjgHIBBWgbjgDcBDAGwFdgaA7bp04BfQiXKU+dAELM2XXjQDOMKAEt+AcxFNChAGbd+AYxjqI-OCa6cAPABUAfAAoDNFwEosTuA880DuRQwDDcUFYGXmIEhKCQsHBGpuaWScZmFvwAwrZeNBhk4gD0xXDKABYQQgAmcMBQUNBwAEbc8DUQwMpw-BAw4iFhEdZ5Xj7k4jjqBnAuALLsMBUAdFDs-J0Atl7eRUQ4OKW9-XDsysrqWvzsLZyoMBDoc1vqF5pacAAGHDzSgsIvp4pjghuErBRkNIGLRdFMYlMwSNIdRZLQADQKP40WgwbowWF6IgiTxEhEEeLQeDJTJpGrqEJmABKoXB+We+2mswWS1W602EB2nj2U2ODihdDICChNAARPRZSwRLQ4G8TvBzpdrrd7ggnlI6GhaCtCHBcEcymaAApNFCwRB0X68FVq17vbSqqwG2hSqRyhVKlVteAhACO3AZwDqmmlKElsek8sVWKUvSEnEDJoOSIhfrojDhxP0RBzkhlaMxTuheNUhMIMSAA

💻 Code

export type U = 	
	| {type: 'A'; value: null}
	| {type: 'B'; value: string};

function call<T>(f: () => T): T {return f()}

export function functionCall(): U {
	// should error but does not
	return call(() => {
		if (Math.random()) {
			// not assignable to U (missing `value: null`)
			return {type: 'A'};
		}

		return {type: 'B', value: 'test'};
	});
}

export function directReturn(): U {
	if (Math.random()) {
		// Type '{ type: "A"; }' is not assignable to type 'U'.
  		//   Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'.
		return {type: 'A'};
	}

	return {type: 'B', value: 'test'};
}

🙁 Actual behavior

type error in directReturn, but no type error in functionCall

🙂 Expected behavior

type error in both directReturn and functionCall, because {type: 'A'} is not assignable to {type: 'A', value: null}

Additional information about the issue

It does work with exactOptionalPropertyTypes disabled: Playground

With exactOptionalPropertyTypes enabled, the return type of call() is inferred as:

{
    type: "A";
    value?: never;
} | {
    type: "B";
    value: string;
}

whereas with it disabled, it's inferred as:

{
    type: "A";
    value?: undefined;
} | {
    type: "B";
    value: string;
}

But this doesn't fully explain it, because even assigning the result of call() to a variable before returning it causes the expected error:

export function functionCall(): U {
	const result = call(() => {
		if (Math.random()) {
			// not assignable to U (missing `value: null`)
			return {type: 'A'};
		}

		return {type: 'B', value: 'test'};
	});
	// error as expected
	return result;
}

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions