diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index dfd479127202c..9e50f64d094da 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -6258,9 +6258,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.JSDocPropertyTag:
+ if (!isOptionalDeclaration(declaration)) {
+ return false;
+ }
symbol ??= getSymbolOfDeclaration(declaration);
+ if (!(symbol.flags & SymbolFlags.Property) || !(symbol as MappedSymbol).links?.mappedType) {
+ return false;
+ }
const type = getTypeOfSymbol(symbol);
- return !!(symbol.flags & SymbolFlags.Property && symbol.flags & SymbolFlags.Optional && isOptionalDeclaration(declaration) && (symbol as MappedSymbol).links?.mappedType && containsNonMissingUndefinedType(type));
+ if (!containsNonMissingUndefinedType(type)) {
+ return false;
+ }
+ const declaredType = getEffectiveTypeAnnotationNode(declaration);
+ return !!(declaredType && !containsUndefinedType(getTypeFromTypeNodeWithoutContext(declaredType)));
case SyntaxKind.Parameter:
case SyntaxKind.JSDocParameterTag:
return requiresAddingImplicitUndefined(declaration, enclosingDeclaration);
diff --git a/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined1.ts b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined1.ts
new file mode 100644
index 0000000000000..7e739e9531e83
--- /dev/null
+++ b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined1.ts
@@ -0,0 +1,29 @@
+///
+
+// @strict: true
+// @exactOptionalPropertyTypes: true
+
+// https://github.com/microsoft/TypeScript/issues/59948
+
+//// type OptionalToUnionWithUndefined = {
+//// [K in keyof T]: T extends Record ? T[K] : T[K] | undefined;
+//// };
+////
+//// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>;
+//// type Literal/*2*/ = { a?: string | undefined };
+////
+//// type Res1/*3*/ = Required;
+//// type Res2/*4*/ = Required;
+
+verify.quickInfoAt("1", `type Intermidiate = {
+ a?: string | undefined;
+}`);
+verify.quickInfoAt("2", `type Literal = {
+ a?: string | undefined;
+}`);
+verify.quickInfoAt("3", `type Res1 = {
+ a: string | undefined;
+}`);
+verify.quickInfoAt("4", `type Res2 = {
+ a: string | undefined;
+}`);
diff --git a/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined2.ts b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined2.ts
new file mode 100644
index 0000000000000..a5a68ba748dee
--- /dev/null
+++ b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined2.ts
@@ -0,0 +1,26 @@
+///
+
+// @strict: true
+
+//// type OptionalToUnionWithUndefined = {
+//// [K in keyof T]: T extends Record ? T[K] : T[K] | undefined;
+//// };
+////
+//// type Intermidiate/*1*/ = OptionalToUnionWithUndefined<{ a?: string }>;
+//// type Literal/*2*/ = { a?: string | undefined };
+////
+//// type Res1/*3*/ = Required;
+//// type Res2/*4*/ = Required;
+
+verify.quickInfoAt("1", `type Intermidiate = {
+ a?: string | undefined;
+}`);
+verify.quickInfoAt("2", `type Literal = {
+ a?: string | undefined;
+}`);
+verify.quickInfoAt("3", `type Res1 = {
+ a: string;
+}`);
+verify.quickInfoAt("4", `type Res2 = {
+ a: string;
+}`);
diff --git a/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined3.ts b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined3.ts
new file mode 100644
index 0000000000000..0a23a8053eb37
--- /dev/null
+++ b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined3.ts
@@ -0,0 +1,13 @@
+///
+
+// https://github.com/microsoft/TypeScript/issues/60411
+
+// @strict: true
+
+//// type UnsetUndefinedToOblivion = { [P in keyof T]-?: T[P] | undefined };
+//// type SetUndefined = { [P in keyof T]: T[P] | undefined };
+//// type TheWhat/**/ = SetUndefined>;
+
+verify.quickInfoAt("", `type TheWhat = {
+ a: 1 | undefined;
+}`);
diff --git a/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined4.ts b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined4.ts
new file mode 100644
index 0000000000000..f71219acb829d
--- /dev/null
+++ b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined4.ts
@@ -0,0 +1,11 @@
+// @strict: true
+
+//// type A/*1*/ = { [K in keyof { a?: string }]-?: string }
+//// type B/*2*/ = { [K in keyof A]: string | undefined }
+
+verify.quickInfoAt("1", `type A = {
+ a: string;
+}`);
+verify.quickInfoAt("2", `type B = {
+ a: string | undefined;
+}`);
diff --git a/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined5.ts b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined5.ts
new file mode 100644
index 0000000000000..d4b9edee4d3d0
--- /dev/null
+++ b/tests/cases/fourslash/quickInfoMappedPropertyUnionUndefined5.ts
@@ -0,0 +1,23 @@
+// @strict: true
+
+// https://github.com/microsoft/TypeScript/issues/62325
+
+//// type RequiredKeys = {
+//// [K in keyof Required]: T[K];
+//// };
+////
+//// type Foo = {
+//// a?: string;
+//// b?: number;
+//// c: string;
+//// d: boolean | undefined;
+//// };
+////
+//// type Bar/*1*/ = RequiredKeys;
+
+verify.quickInfoAt("1", `type Bar = {
+ a: string | undefined;
+ b: number | undefined;
+ c: string;
+ d: boolean | undefined;
+}`);