Skip to content

Removing optional modifier in a mapped type behaves inconsistently b/w array vs objectΒ #63291

@juhort

Description

@juhort

πŸ”Ž Search Terms

This doesn't seem related to #31025. This is about the inconsistent behavior and specifically when exactOptionalPropertyTypes option is enabled.

πŸ•— Version & Regression Information

Fails with v5.9.3.

⏯ Playground Link

https://www.typescriptlang.org/play/?exactOptionalPropertyTypes=true&ts=5.3.3#code/PTAEAEFMA8EMGMAuB5ADoglgewHawDYAKATlqpMYgJ4AqV5AzgFyiLECukAUF9eaACVIARwA8NAHygAvKADeAbUKgMOUAGtIVLADNQNALoBaAPwsaSgwF9QIUAGVYAW0ihYDVgAtXAI3YZ8RCNVUAADIWF-YkgAE1DWem4uOxJIBgoANzSvV1CAH1B2HBjIHVVY0IAaUBisNJwAckRQBgB3AnwsVpVmwFByXkTQAHUMRE9kHwArSCQZQREo2NE5WDMWtlUAc1ACopKynFirCWTgaSk5NxYGDZxt3eLS8piAblArHjt7dvxO1o8xrkHvtnvF+nxXCMxgBBYjEWBUOYRRYxUQKAAUN2IWx2hUeB1iAEoTAYTiBzqAFFitgYgA

πŸ’» Code

// @exactOptionalPropertyTypes: true

type Req<T> = {[P in keyof T]-?: T[P]} // Same as the built-in `Required` type

// Preserves the `| undefined`, doesn't swallow it βœ…
type WithObject = Required<{a?: string | undefined}>
//=> { a: string | undefined; }

// Swallows the `| undefined` βœ…
type WithArray = Required<[(string | undefined)?]>
//=> [string]

πŸ™ Actual behavior

With exactOptionalPropertyTypes enabled, ?: string | undefined is different than ?: string. And mapped types respect this differentiation by not swallowing the | undefined with the -? mapping modifier.

// @exactOptionalPropertyTypes: true

type Req<T> = {[P in keyof T]-?: T[P]} // Same as the built-in `Required` type

// Preserves the `| undefined`, doesn't swallow it βœ…
type WithObject = Req<{a?: string | undefined}> 
//=> { a: string | undefined; }

However, when instantiated with arrays, the | undefined is swallowed during mapping. This is the issue!

// @exactOptionalPropertyTypes: true

type Req<T> = {[P in keyof T]-?: T[P]} // Same as the built-in `Required` type

// Swallows the `| undefined` ❌
type WithArray = Req<[(string | undefined)?]> 
//=> [string]

And this is not just a display issue:

const withObject: WithObject = {a: undefined}; // βœ… Fine
 
const withArray: WithArray = [undefined]; // ❌ Not fine

When exactOptionalPropertyTypes is disabled, the | undefined gets swallowed, and that is fine and unrelated to this particular issue.


// @exactOptionalPropertyTypes: false

type Req<T> = {[P in keyof T]-?: T[P]} // Same as the built-in `Required` type

// Swallows the `| undefined` βœ…
type WithObject = Req<{a?: string | undefined}> 
//=> { a: string; }

πŸ™‚ Expected behavior

The behavior should be consistent. The | undefined bit should not be swallowed when mapping even for arrays.

Additional information about the issue

Here's another example:

// @exactOptionalPropertyTypes: true

type ToStringOrUnd<T> = {[P in keyof T]-?: string | undefined}

// Preserves the `| undefined` βœ…
type WithObject = ToStringOrUnd<{a: 1; b?: 2}> 
//=> { a: string | undefined; b: string | undefined; }

// Preserves the `| undefined` for the required element βœ…
// But, doesn't preserve it for the optional element ❌
type WithArray = ToStringOrUnd<[1, 2?]> 
//=> [string | undefined, string]

Playground Link

The -? mapping modifier seems to swallow the | undefined when operating on arrays, and specifically when operating with optional array elements.

Metadata

Metadata

Assignees

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions