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; +}`);