From 3e25424652b0151f01463d7b598166377ad76f16 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Thu, 29 Apr 2021 19:20:40 +0300 Subject: [PATCH] fix(43408): emit nullable/optional types on getters (#43476) --- src/compiler/checker.ts | 2 +- ...clarationsReusesExistingTypeAnnotations.js | 237 ++++++++++++++++++ ...tionsReusesExistingTypeAnnotations.symbols | 193 ++++++++++++++ ...rationsReusesExistingTypeAnnotations.types | 207 +++++++++++++++ ...clarationsReusesExistingTypeAnnotations.ts | 109 ++++++++ 5 files changed, 747 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.js create mode 100644 tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.symbols create mode 100644 tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.types create mode 100644 tests/cases/conformance/jsdoc/declarations/jsDeclarationsReusesExistingTypeAnnotations.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bbd7def0d78..47cc9c27b88 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6004,7 +6004,7 @@ namespace ts { function serializeTypeForDeclaration(context: NodeBuilderContext, type: Type, symbol: Symbol, enclosingDeclaration: Node | undefined, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean) { if (type !== errorType && enclosingDeclaration) { const declWithExistingAnnotation = getDeclarationWithTypeAnnotation(symbol, enclosingDeclaration); - if (declWithExistingAnnotation && !isFunctionLikeDeclaration(declWithExistingAnnotation)) { + if (declWithExistingAnnotation && !isFunctionLikeDeclaration(declWithExistingAnnotation) && !isGetAccessorDeclaration(declWithExistingAnnotation)) { // try to reuse the existing annotation const existing = getEffectiveTypeAnnotationNode(declWithExistingAnnotation)!; if (getTypeFromTypeNode(existing) === type && existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, type)) { diff --git a/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.js b/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.js new file mode 100644 index 00000000000..9d85336ed5c --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.js @@ -0,0 +1,237 @@ +//// [index.js] +class С1 { + /** @type {string=} */ + p1 = undefined; + + /** @type {string | undefined} */ + p2 = undefined; + + /** @type {?string} */ + p3 = null; + + /** @type {string | null} */ + p4 = null; +} + +class С2 { + /** @type {string=} */ + get p1() { + return undefined; + } + + /** @type {string | undefined} */ + get p2() { + return undefined; + } + + /** @type {?string} */ + get p3() { + return null; + } + + /** @type {string | null} */ + get p4() { + return null; + } +} + + +class С3 { + /** @type {string=} */ + get p1() { + return undefined; + } + + /** @param {string=} value */ + set p1(value) { + this.p1 = value; + } + + /** @type {string | undefined} */ + get p2() { + return undefined; + } + + /** @param {string | undefined} value */ + set p2(value) { + this.p2 = value; + } + + /** @type {?string} */ + get p3() { + return null; + } + + /** @param {?string} value */ + set p3(value) { + this.p3 = value; + } + + /** @type {string | null} */ + get p4() { + return null; + } + + /** @param {string | null} value */ + set p4(value) { + this.p4 = value; + } +} + + +class С4 { + /** @param {string=} value */ + set p1(value) { + this.p1 = value; + } + + /** @param {string | undefined} value */ + set p2(value) { + this.p2 = value; + } + + /** @param {?string} value */ + set p3(value) { + this.p3 = value; + } + + /** @param {string | null} value */ + set p4(value) { + this.p4 = value; + } +} + + +//// [index.js] +"use strict"; +class С1 { + /** @type {string=} */ + p1 = undefined; + /** @type {string | undefined} */ + p2 = undefined; + /** @type {?string} */ + p3 = null; + /** @type {string | null} */ + p4 = null; +} +class С2 { + /** @type {string=} */ + get p1() { + return undefined; + } + /** @type {string | undefined} */ + get p2() { + return undefined; + } + /** @type {?string} */ + get p3() { + return null; + } + /** @type {string | null} */ + get p4() { + return null; + } +} +class С3 { + /** @type {string=} */ + get p1() { + return undefined; + } + /** @param {string=} value */ + set p1(value) { + this.p1 = value; + } + /** @type {string | undefined} */ + get p2() { + return undefined; + } + /** @param {string | undefined} value */ + set p2(value) { + this.p2 = value; + } + /** @type {?string} */ + get p3() { + return null; + } + /** @param {?string} value */ + set p3(value) { + this.p3 = value; + } + /** @type {string | null} */ + get p4() { + return null; + } + /** @param {string | null} value */ + set p4(value) { + this.p4 = value; + } +} +class С4 { + /** @param {string=} value */ + set p1(value) { + this.p1 = value; + } + /** @param {string | undefined} value */ + set p2(value) { + this.p2 = value; + } + /** @param {?string} value */ + set p3(value) { + this.p3 = value; + } + /** @param {string | null} value */ + set p4(value) { + this.p4 = value; + } +} + + +//// [index.d.ts] +declare class С1 { + /** @type {string=} */ + p1: string | undefined; + /** @type {string | undefined} */ + p2: string | undefined; + /** @type {?string} */ + p3: string | null; + /** @type {string | null} */ + p4: string | null; +} +declare class С2 { + /** @type {string=} */ + get p1(): string | undefined; + /** @type {string | undefined} */ + get p2(): string | undefined; + /** @type {?string} */ + get p3(): string | null; + /** @type {string | null} */ + get p4(): string | null; +} +declare class С3 { + /** @param {string=} value */ + set p1(arg: string | undefined); + /** @type {string=} */ + get p1(): string | undefined; + /** @param {string | undefined} value */ + set p2(arg: string | undefined); + /** @type {string | undefined} */ + get p2(): string | undefined; + /** @param {?string} value */ + set p3(arg: string | null); + /** @type {?string} */ + get p3(): string | null; + /** @param {string | null} value */ + set p4(arg: string | null); + /** @type {string | null} */ + get p4(): string | null; +} +declare class С4 { + /** @param {string=} value */ + set p1(arg: string | undefined); + /** @param {string | undefined} value */ + set p2(arg: string | undefined); + /** @param {?string} value */ + set p3(arg: string | null); + /** @param {string | null} value */ + set p4(arg: string | null); +} diff --git a/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.symbols b/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.symbols new file mode 100644 index 00000000000..3f400fbe146 --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.symbols @@ -0,0 +1,193 @@ +=== tests/cases/conformance/jsdoc/declarations/index.js === +class С1 { +>С1 : Symbol(С1, Decl(index.js, 0, 0)) + + /** @type {string=} */ + p1 = undefined; +>p1 : Symbol(С1.p1, Decl(index.js, 0, 10)) +>undefined : Symbol(undefined) + + /** @type {string | undefined} */ + p2 = undefined; +>p2 : Symbol(С1.p2, Decl(index.js, 2, 19)) +>undefined : Symbol(undefined) + + /** @type {?string} */ + p3 = null; +>p3 : Symbol(С1.p3, Decl(index.js, 5, 19)) + + /** @type {string | null} */ + p4 = null; +>p4 : Symbol(С1.p4, Decl(index.js, 8, 14)) +} + +class С2 { +>С2 : Symbol(С2, Decl(index.js, 12, 1)) + + /** @type {string=} */ + get p1() { +>p1 : Symbol(С2.p1, Decl(index.js, 14, 10)) + + return undefined; +>undefined : Symbol(undefined) + } + + /** @type {string | undefined} */ + get p2() { +>p2 : Symbol(С2.p2, Decl(index.js, 18, 5)) + + return undefined; +>undefined : Symbol(undefined) + } + + /** @type {?string} */ + get p3() { +>p3 : Symbol(С2.p3, Decl(index.js, 23, 5)) + + return null; + } + + /** @type {string | null} */ + get p4() { +>p4 : Symbol(С2.p4, Decl(index.js, 28, 5)) + + return null; + } +} + + +class С3 { +>С3 : Symbol(С3, Decl(index.js, 34, 1)) + + /** @type {string=} */ + get p1() { +>p1 : Symbol(С3.p1, Decl(index.js, 37, 10), Decl(index.js, 41, 5)) + + return undefined; +>undefined : Symbol(undefined) + } + + /** @param {string=} value */ + set p1(value) { +>p1 : Symbol(С3.p1, Decl(index.js, 37, 10), Decl(index.js, 41, 5)) +>value : Symbol(value, Decl(index.js, 44, 11)) + + this.p1 = value; +>this.p1 : Symbol(С3.p1, Decl(index.js, 37, 10), Decl(index.js, 41, 5)) +>this : Symbol(С3, Decl(index.js, 34, 1)) +>p1 : Symbol(С3.p1, Decl(index.js, 37, 10), Decl(index.js, 41, 5)) +>value : Symbol(value, Decl(index.js, 44, 11)) + } + + /** @type {string | undefined} */ + get p2() { +>p2 : Symbol(С3.p2, Decl(index.js, 46, 5), Decl(index.js, 51, 5)) + + return undefined; +>undefined : Symbol(undefined) + } + + /** @param {string | undefined} value */ + set p2(value) { +>p2 : Symbol(С3.p2, Decl(index.js, 46, 5), Decl(index.js, 51, 5)) +>value : Symbol(value, Decl(index.js, 54, 11)) + + this.p2 = value; +>this.p2 : Symbol(С3.p2, Decl(index.js, 46, 5), Decl(index.js, 51, 5)) +>this : Symbol(С3, Decl(index.js, 34, 1)) +>p2 : Symbol(С3.p2, Decl(index.js, 46, 5), Decl(index.js, 51, 5)) +>value : Symbol(value, Decl(index.js, 54, 11)) + } + + /** @type {?string} */ + get p3() { +>p3 : Symbol(С3.p3, Decl(index.js, 56, 5), Decl(index.js, 61, 5)) + + return null; + } + + /** @param {?string} value */ + set p3(value) { +>p3 : Symbol(С3.p3, Decl(index.js, 56, 5), Decl(index.js, 61, 5)) +>value : Symbol(value, Decl(index.js, 64, 11)) + + this.p3 = value; +>this.p3 : Symbol(С3.p3, Decl(index.js, 56, 5), Decl(index.js, 61, 5)) +>this : Symbol(С3, Decl(index.js, 34, 1)) +>p3 : Symbol(С3.p3, Decl(index.js, 56, 5), Decl(index.js, 61, 5)) +>value : Symbol(value, Decl(index.js, 64, 11)) + } + + /** @type {string | null} */ + get p4() { +>p4 : Symbol(С3.p4, Decl(index.js, 66, 5), Decl(index.js, 71, 5)) + + return null; + } + + /** @param {string | null} value */ + set p4(value) { +>p4 : Symbol(С3.p4, Decl(index.js, 66, 5), Decl(index.js, 71, 5)) +>value : Symbol(value, Decl(index.js, 74, 11)) + + this.p4 = value; +>this.p4 : Symbol(С3.p4, Decl(index.js, 66, 5), Decl(index.js, 71, 5)) +>this : Symbol(С3, Decl(index.js, 34, 1)) +>p4 : Symbol(С3.p4, Decl(index.js, 66, 5), Decl(index.js, 71, 5)) +>value : Symbol(value, Decl(index.js, 74, 11)) + } +} + + +class С4 { +>С4 : Symbol(С4, Decl(index.js, 77, 1)) + + /** @param {string=} value */ + set p1(value) { +>p1 : Symbol(С4.p1, Decl(index.js, 80, 10)) +>value : Symbol(value, Decl(index.js, 82, 11)) + + this.p1 = value; +>this.p1 : Symbol(С4.p1, Decl(index.js, 80, 10)) +>this : Symbol(С4, Decl(index.js, 77, 1)) +>p1 : Symbol(С4.p1, Decl(index.js, 80, 10)) +>value : Symbol(value, Decl(index.js, 82, 11)) + } + + /** @param {string | undefined} value */ + set p2(value) { +>p2 : Symbol(С4.p2, Decl(index.js, 84, 5)) +>value : Symbol(value, Decl(index.js, 87, 11)) + + this.p2 = value; +>this.p2 : Symbol(С4.p2, Decl(index.js, 84, 5)) +>this : Symbol(С4, Decl(index.js, 77, 1)) +>p2 : Symbol(С4.p2, Decl(index.js, 84, 5)) +>value : Symbol(value, Decl(index.js, 87, 11)) + } + + /** @param {?string} value */ + set p3(value) { +>p3 : Symbol(С4.p3, Decl(index.js, 89, 5)) +>value : Symbol(value, Decl(index.js, 92, 11)) + + this.p3 = value; +>this.p3 : Symbol(С4.p3, Decl(index.js, 89, 5)) +>this : Symbol(С4, Decl(index.js, 77, 1)) +>p3 : Symbol(С4.p3, Decl(index.js, 89, 5)) +>value : Symbol(value, Decl(index.js, 92, 11)) + } + + /** @param {string | null} value */ + set p4(value) { +>p4 : Symbol(С4.p4, Decl(index.js, 94, 5)) +>value : Symbol(value, Decl(index.js, 97, 11)) + + this.p4 = value; +>this.p4 : Symbol(С4.p4, Decl(index.js, 94, 5)) +>this : Symbol(С4, Decl(index.js, 77, 1)) +>p4 : Symbol(С4.p4, Decl(index.js, 94, 5)) +>value : Symbol(value, Decl(index.js, 97, 11)) + } +} + diff --git a/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.types b/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.types new file mode 100644 index 00000000000..63880613f1e --- /dev/null +++ b/tests/baselines/reference/jsDeclarationsReusesExistingTypeAnnotations.types @@ -0,0 +1,207 @@ +=== tests/cases/conformance/jsdoc/declarations/index.js === +class С1 { +>С1 : С1 + + /** @type {string=} */ + p1 = undefined; +>p1 : string | undefined +>undefined : undefined + + /** @type {string | undefined} */ + p2 = undefined; +>p2 : string | undefined +>undefined : undefined + + /** @type {?string} */ + p3 = null; +>p3 : string | null +>null : null + + /** @type {string | null} */ + p4 = null; +>p4 : string | null +>null : null +} + +class С2 { +>С2 : С2 + + /** @type {string=} */ + get p1() { +>p1 : string | undefined + + return undefined; +>undefined : undefined + } + + /** @type {string | undefined} */ + get p2() { +>p2 : string | undefined + + return undefined; +>undefined : undefined + } + + /** @type {?string} */ + get p3() { +>p3 : string | null + + return null; +>null : null + } + + /** @type {string | null} */ + get p4() { +>p4 : string | null + + return null; +>null : null + } +} + + +class С3 { +>С3 : С3 + + /** @type {string=} */ + get p1() { +>p1 : string | undefined + + return undefined; +>undefined : undefined + } + + /** @param {string=} value */ + set p1(value) { +>p1 : string | undefined +>value : string | undefined + + this.p1 = value; +>this.p1 = value : string | undefined +>this.p1 : string | undefined +>this : this +>p1 : string | undefined +>value : string | undefined + } + + /** @type {string | undefined} */ + get p2() { +>p2 : string | undefined + + return undefined; +>undefined : undefined + } + + /** @param {string | undefined} value */ + set p2(value) { +>p2 : string | undefined +>value : string | undefined + + this.p2 = value; +>this.p2 = value : string | undefined +>this.p2 : string | undefined +>this : this +>p2 : string | undefined +>value : string | undefined + } + + /** @type {?string} */ + get p3() { +>p3 : string | null + + return null; +>null : null + } + + /** @param {?string} value */ + set p3(value) { +>p3 : string | null +>value : string | null + + this.p3 = value; +>this.p3 = value : string | null +>this.p3 : string | null +>this : this +>p3 : string | null +>value : string | null + } + + /** @type {string | null} */ + get p4() { +>p4 : string | null + + return null; +>null : null + } + + /** @param {string | null} value */ + set p4(value) { +>p4 : string | null +>value : string | null + + this.p4 = value; +>this.p4 = value : string | null +>this.p4 : string | null +>this : this +>p4 : string | null +>value : string | null + } +} + + +class С4 { +>С4 : С4 + + /** @param {string=} value */ + set p1(value) { +>p1 : string | undefined +>value : string | undefined + + this.p1 = value; +>this.p1 = value : string | undefined +>this.p1 : string | undefined +>this : this +>p1 : string | undefined +>value : string | undefined + } + + /** @param {string | undefined} value */ + set p2(value) { +>p2 : string | undefined +>value : string | undefined + + this.p2 = value; +>this.p2 = value : string | undefined +>this.p2 : string | undefined +>this : this +>p2 : string | undefined +>value : string | undefined + } + + /** @param {?string} value */ + set p3(value) { +>p3 : string | null +>value : string | null + + this.p3 = value; +>this.p3 = value : string | null +>this.p3 : string | null +>this : this +>p3 : string | null +>value : string | null + } + + /** @param {string | null} value */ + set p4(value) { +>p4 : string | null +>value : string | null + + this.p4 = value; +>this.p4 = value : string | null +>this.p4 : string | null +>this : this +>p4 : string | null +>value : string | null + } +} + diff --git a/tests/cases/conformance/jsdoc/declarations/jsDeclarationsReusesExistingTypeAnnotations.ts b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsReusesExistingTypeAnnotations.ts new file mode 100644 index 00000000000..f9326e4644e --- /dev/null +++ b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsReusesExistingTypeAnnotations.ts @@ -0,0 +1,109 @@ +// @allowJs: true +// @checkJs: true +// @target: esnext +// @strict: true +// @declaration: true +// @filename: index.js +// @outDir: /out + +class С1 { + /** @type {string=} */ + p1 = undefined; + + /** @type {string | undefined} */ + p2 = undefined; + + /** @type {?string} */ + p3 = null; + + /** @type {string | null} */ + p4 = null; +} + +class С2 { + /** @type {string=} */ + get p1() { + return undefined; + } + + /** @type {string | undefined} */ + get p2() { + return undefined; + } + + /** @type {?string} */ + get p3() { + return null; + } + + /** @type {string | null} */ + get p4() { + return null; + } +} + + +class С3 { + /** @type {string=} */ + get p1() { + return undefined; + } + + /** @param {string=} value */ + set p1(value) { + this.p1 = value; + } + + /** @type {string | undefined} */ + get p2() { + return undefined; + } + + /** @param {string | undefined} value */ + set p2(value) { + this.p2 = value; + } + + /** @type {?string} */ + get p3() { + return null; + } + + /** @param {?string} value */ + set p3(value) { + this.p3 = value; + } + + /** @type {string | null} */ + get p4() { + return null; + } + + /** @param {string | null} value */ + set p4(value) { + this.p4 = value; + } +} + + +class С4 { + /** @param {string=} value */ + set p1(value) { + this.p1 = value; + } + + /** @param {string | undefined} value */ + set p2(value) { + this.p2 = value; + } + + /** @param {?string} value */ + set p3(value) { + this.p3 = value; + } + + /** @param {string | null} value */ + set p4(value) { + this.p4 = value; + } +}