Fixed an issue with mapped property symbol not displaying added | undefined when its origin symbol was optional#59957
Conversation
…defined` when its origin symbol was optional
There was a problem hiding this comment.
Pull Request Overview
This PR fixes an issue where mapped property symbols weren't displaying | undefined when their origin symbol was optional. The fix ensures that when TypeScript displays type information for mapped types, it correctly shows the union with undefined for properties that originated from optional properties.
- Refactors the
requiresAddingImplicitUndefinedfunction to improve readability and fix the logic for determining when to add implicit undefined - Adds comprehensive test coverage with 5 new fourslash tests covering various scenarios of mapped types with optional properties
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/compiler/checker.ts | Refactors the logic in requiresAddingImplicitUndefined to use early returns and adds proper type checking for declared types |
| tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined1.ts | Tests mapped type behavior with exactOptionalPropertyTypes enabled |
| tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined2.ts | Tests mapped type behavior in standard strict mode |
| tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined3.ts | Tests nested mapped type transformations |
| tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined4.ts | Tests simple mapped type with required modifier |
| tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined5.ts | Tests RequiredKeys utility type scenario |
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | ||
| //// type Literal/*2*/ = { a?: string | undefined }; | ||
| //// | ||
| //// type Res1/*3*/ = Required<Intermidiate>; | ||
| //// type Res2/*4*/ = Required<Literal>; | ||
|
|
||
| verify.quickInfoAt("1", `type Intermidiate = { |
There was a problem hiding this comment.
The type name 'Intermidiate' is misspelled. It should be 'Intermediate'.
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermidiate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermidiate = { | |
| //// type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermediate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermediate = { |
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | ||
| //// type Literal/*2*/ = { a?: string | undefined }; | ||
| //// | ||
| //// type Res1/*3*/ = Required<Intermidiate>; | ||
| //// type Res2/*4*/ = Required<Literal>; | ||
|
|
||
| verify.quickInfoAt("1", `type Intermidiate = { |
There was a problem hiding this comment.
The type name 'Intermidiate' is misspelled. It should be 'Intermediate'.
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermidiate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermidiate = { | |
| //// type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermediate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermediate = { |
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | ||
| //// type Literal/*2*/ = { a?: string | undefined }; | ||
| //// | ||
| //// type Res1/*3*/ = Required<Intermidiate>; | ||
| //// type Res2/*4*/ = Required<Literal>; | ||
|
|
||
| verify.quickInfoAt("1", `type Intermidiate = { |
There was a problem hiding this comment.
The type name 'Intermidiate' is misspelled. It should be 'Intermediate'.
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermidiate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermidiate = { | |
| //// type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermediate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermediate = { |
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | ||
| //// type Literal/*2*/ = { a?: string | undefined }; | ||
| //// | ||
| //// type Res1/*3*/ = Required<Intermidiate>; | ||
| //// type Res2/*4*/ = Required<Literal>; | ||
|
|
||
| verify.quickInfoAt("1", `type Intermidiate = { |
There was a problem hiding this comment.
The type name 'Intermidiate' is misspelled. It should be 'Intermediate'.
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermidiate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermidiate = { | |
| //// type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermediate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermediate = { |
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | ||
| //// type Literal/*2*/ = { a?: string | undefined }; | ||
| //// | ||
| //// type Res1/*3*/ = Required<Intermidiate>; | ||
| //// type Res2/*4*/ = Required<Literal>; | ||
|
|
||
| verify.quickInfoAt("1", `type Intermidiate = { |
There was a problem hiding this comment.
The type name 'Intermidiate' is misspelled. It should be 'Intermediate'.
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermidiate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermidiate = { | |
| //// type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermediate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermediate = { |
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | ||
| //// type Literal/*2*/ = { a?: string | undefined }; | ||
| //// | ||
| //// type Res1/*3*/ = Required<Intermidiate>; | ||
| //// type Res2/*4*/ = Required<Literal>; | ||
|
|
||
| verify.quickInfoAt("1", `type Intermidiate = { |
There was a problem hiding this comment.
The type name 'Intermidiate' is misspelled. It should be 'Intermediate'.
| //// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermidiate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermidiate = { | |
| //// type Intermediate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>; | |
| //// type Literal/*2*/ = { a?: string | undefined }; | |
| //// | |
| //// type Res1/*3*/ = Required<Intermediate>; | |
| //// type Res2/*4*/ = Required<Literal>; | |
| verify.quickInfoAt("1", `type Intermediate = { |
fixes #59948
fixes #60411
fixes #62325