Instantiate getter when infering setter parameter value (#43564)

* Instantiate getter when infering setter parameter value

* Use esnext on tests

* Instantiate for JsDoc and getter from body

* PR comments

* Updated baseline
This commit is contained in:
Armando Aguirre 2021-04-19 23:23:40 -07:00 committed by GitHub
parent 167ebcd93b
commit f67ee44379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 411 additions and 18 deletions

View File

@ -9082,42 +9082,36 @@ namespace ts {
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
const setterType = getAnnotatedAccessorType(setter);
// For write operations, prioritize type annotations on the setter
if (writing) {
const setterParameterType = getAnnotatedAccessorType(setter);
if (setterParameterType) {
const flags = getCheckFlags(symbol);
if (flags & CheckFlags.Instantiated) {
const links = getSymbolLinks(symbol);
return instantiateType(setterParameterType, links.mapper);
}
return setterParameterType;
}
if (writing && setterType) {
return instantiateTypeIfNeeded(setterType, symbol);
}
// Else defer to the getter type
if (getter && isInJSFile(getter)) {
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
if (jsDocType) {
return jsDocType;
return instantiateTypeIfNeeded(jsDocType, symbol);
}
}
// Try to see if the user specified a return type on the get-accessor.
const getterReturnType = getAnnotatedAccessorType(getter);
if (getterReturnType) {
return getterReturnType;
const getterType = getAnnotatedAccessorType(getter);
if (getterType) {
return instantiateTypeIfNeeded(getterType, symbol);
}
// If the user didn't specify a return type, try to use the set-accessor's parameter type.
const setterParameterType = getAnnotatedAccessorType(setter);
if (setterParameterType) {
return setterParameterType;
if (setterType) {
return setterType;
}
// If there are no specified types, try to infer it from the body of the get accessor if it exists.
if (getter && getter.body) {
return getReturnTypeFromBody(getter);
const returnTypeFromBody = getReturnTypeFromBody(getter);
return instantiateTypeIfNeeded(returnTypeFromBody, symbol);
}
// Otherwise, fall back to 'any'.
@ -9135,6 +9129,15 @@ namespace ts {
return anyType;
}
return undefined;
function instantiateTypeIfNeeded(type: Type, symbol: Symbol) {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
const links = getSymbolLinks(symbol);
return instantiateType(type, links.mapper);
}
return type;
}
}
function getBaseTypeVariableOfClass(symbol: Symbol) {

View File

@ -0,0 +1,49 @@
//// [genericSetterInClassType.ts]
module Generic {
class C<T> {
get y(): T {
return 1 as never;
}
set y(v) { }
}
var c = new C<number>();
c.y = c.y;
class Box<T> {
#value!: T;
get value() {
return this.#value;
}
set value(value) {
this.#value = value;
}
}
new Box<number>().value = 3;
}
//// [genericSetterInClassType.js]
var Generic;
(function (Generic) {
class C {
get y() {
return 1;
}
set y(v) { }
}
var c = new C();
c.y = c.y;
class Box {
#value;
get value() {
return this.#value;
}
set value(value) {
this.#value = value;
}
}
new Box().value = 3;
})(Generic || (Generic = {}));

View File

@ -0,0 +1,63 @@
=== tests/cases/conformance/classes/members/classTypes/genericSetterInClassType.ts ===
module Generic {
>Generic : Symbol(Generic, Decl(genericSetterInClassType.ts, 0, 0))
class C<T> {
>C : Symbol(C, Decl(genericSetterInClassType.ts, 0, 16))
>T : Symbol(T, Decl(genericSetterInClassType.ts, 1, 12))
get y(): T {
>y : Symbol(C.y, Decl(genericSetterInClassType.ts, 1, 16), Decl(genericSetterInClassType.ts, 4, 9))
>T : Symbol(T, Decl(genericSetterInClassType.ts, 1, 12))
return 1 as never;
}
set y(v) { }
>y : Symbol(C.y, Decl(genericSetterInClassType.ts, 1, 16), Decl(genericSetterInClassType.ts, 4, 9))
>v : Symbol(v, Decl(genericSetterInClassType.ts, 5, 14))
}
var c = new C<number>();
>c : Symbol(c, Decl(genericSetterInClassType.ts, 8, 7))
>C : Symbol(C, Decl(genericSetterInClassType.ts, 0, 16))
c.y = c.y;
>c.y : Symbol(C.y, Decl(genericSetterInClassType.ts, 1, 16), Decl(genericSetterInClassType.ts, 4, 9))
>c : Symbol(c, Decl(genericSetterInClassType.ts, 8, 7))
>y : Symbol(C.y, Decl(genericSetterInClassType.ts, 1, 16), Decl(genericSetterInClassType.ts, 4, 9))
>c.y : Symbol(C.y, Decl(genericSetterInClassType.ts, 1, 16), Decl(genericSetterInClassType.ts, 4, 9))
>c : Symbol(c, Decl(genericSetterInClassType.ts, 8, 7))
>y : Symbol(C.y, Decl(genericSetterInClassType.ts, 1, 16), Decl(genericSetterInClassType.ts, 4, 9))
class Box<T> {
>Box : Symbol(Box, Decl(genericSetterInClassType.ts, 9, 14))
>T : Symbol(T, Decl(genericSetterInClassType.ts, 11, 14))
#value!: T;
>#value : Symbol(Box.#value, Decl(genericSetterInClassType.ts, 11, 18))
>T : Symbol(T, Decl(genericSetterInClassType.ts, 11, 14))
get value() {
>value : Symbol(Box.value, Decl(genericSetterInClassType.ts, 12, 19), Decl(genericSetterInClassType.ts, 16, 9))
return this.#value;
>this.#value : Symbol(Box.#value, Decl(genericSetterInClassType.ts, 11, 18))
>this : Symbol(Box, Decl(genericSetterInClassType.ts, 9, 14))
}
set value(value) {
>value : Symbol(Box.value, Decl(genericSetterInClassType.ts, 12, 19), Decl(genericSetterInClassType.ts, 16, 9))
>value : Symbol(value, Decl(genericSetterInClassType.ts, 18, 18))
this.#value = value;
>this.#value : Symbol(Box.#value, Decl(genericSetterInClassType.ts, 11, 18))
>this : Symbol(Box, Decl(genericSetterInClassType.ts, 9, 14))
>value : Symbol(value, Decl(genericSetterInClassType.ts, 18, 18))
}
}
new Box<number>().value = 3;
>new Box<number>().value : Symbol(Box.value, Decl(genericSetterInClassType.ts, 12, 19), Decl(genericSetterInClassType.ts, 16, 9))
>Box : Symbol(Box, Decl(genericSetterInClassType.ts, 9, 14))
>value : Symbol(Box.value, Decl(genericSetterInClassType.ts, 12, 19), Decl(genericSetterInClassType.ts, 16, 9))
}

View File

@ -0,0 +1,67 @@
=== tests/cases/conformance/classes/members/classTypes/genericSetterInClassType.ts ===
module Generic {
>Generic : typeof Generic
class C<T> {
>C : C<T>
get y(): T {
>y : T
return 1 as never;
>1 as never : never
>1 : 1
}
set y(v) { }
>y : T
>v : T
}
var c = new C<number>();
>c : C<number>
>new C<number>() : C<number>
>C : typeof C
c.y = c.y;
>c.y = c.y : number
>c.y : number
>c : C<number>
>y : number
>c.y : number
>c : C<number>
>y : number
class Box<T> {
>Box : Box<T>
#value!: T;
>#value : T
get value() {
>value : T
return this.#value;
>this.#value : T
>this : this
}
set value(value) {
>value : T
>value : T
this.#value = value;
>this.#value = value : T
>this.#value : T
>this : this
>value : T
}
}
new Box<number>().value = 3;
>new Box<number>().value = 3 : 3
>new Box<number>().value : number
>new Box<number>() : Box<number>
>Box : typeof Box
>value : number
>3 : 3
}

View File

@ -0,0 +1,58 @@
//// [genericSetterInClassTypeJsDoc.js]
/**
* @template T
*/
class Box {
#value;
/** @param {T} initialValue */
constructor(initialValue) {
this.#value = initialValue;
}
/** @type {T} */
get value() {
return this.#value;
}
set value(value) {
this.#value = value;
}
}
new Box(3).value = 3;
//// [genericSetterInClassTypeJsDoc-out.js]
/**
* @template T
*/
class Box {
#value;
/** @param {T} initialValue */
constructor(initialValue) {
this.#value = initialValue;
}
/** @type {T} */
get value() {
return this.#value;
}
set value(value) {
this.#value = value;
}
}
new Box(3).value = 3;
//// [genericSetterInClassTypeJsDoc-out.d.ts]
/**
* @template T
*/
declare class Box<T> {
/** @param {T} initialValue */
constructor(initialValue: T);
set value(arg: T);
/** @type {T} */
get value(): T;
#private;
}

View File

@ -0,0 +1,45 @@
=== tests/cases/conformance/classes/members/classTypes/genericSetterInClassTypeJsDoc.js ===
/**
* @template T
*/
class Box {
>Box : Symbol(Box, Decl(genericSetterInClassTypeJsDoc.js, 0, 0))
#value;
>#value : Symbol(Box.#value, Decl(genericSetterInClassTypeJsDoc.js, 3, 12))
/** @param {T} initialValue */
constructor(initialValue) {
>initialValue : Symbol(initialValue, Decl(genericSetterInClassTypeJsDoc.js, 7, 16))
this.#value = initialValue;
>this.#value : Symbol(Box.#value, Decl(genericSetterInClassTypeJsDoc.js, 3, 12))
>this : Symbol(Box, Decl(genericSetterInClassTypeJsDoc.js, 0, 0))
>initialValue : Symbol(initialValue, Decl(genericSetterInClassTypeJsDoc.js, 7, 16))
}
/** @type {T} */
get value() {
>value : Symbol(Box.value, Decl(genericSetterInClassTypeJsDoc.js, 9, 5), Decl(genericSetterInClassTypeJsDoc.js, 14, 5))
return this.#value;
>this.#value : Symbol(Box.#value, Decl(genericSetterInClassTypeJsDoc.js, 3, 12))
>this : Symbol(Box, Decl(genericSetterInClassTypeJsDoc.js, 0, 0))
}
set value(value) {
>value : Symbol(Box.value, Decl(genericSetterInClassTypeJsDoc.js, 9, 5), Decl(genericSetterInClassTypeJsDoc.js, 14, 5))
>value : Symbol(value, Decl(genericSetterInClassTypeJsDoc.js, 16, 14))
this.#value = value;
>this.#value : Symbol(Box.#value, Decl(genericSetterInClassTypeJsDoc.js, 3, 12))
>this : Symbol(Box, Decl(genericSetterInClassTypeJsDoc.js, 0, 0))
>value : Symbol(value, Decl(genericSetterInClassTypeJsDoc.js, 16, 14))
}
}
new Box(3).value = 3;
>new Box(3).value : Symbol(Box.value, Decl(genericSetterInClassTypeJsDoc.js, 9, 5), Decl(genericSetterInClassTypeJsDoc.js, 14, 5))
>Box : Symbol(Box, Decl(genericSetterInClassTypeJsDoc.js, 0, 0))
>value : Symbol(Box.value, Decl(genericSetterInClassTypeJsDoc.js, 9, 5), Decl(genericSetterInClassTypeJsDoc.js, 14, 5))

View File

@ -0,0 +1,51 @@
=== tests/cases/conformance/classes/members/classTypes/genericSetterInClassTypeJsDoc.js ===
/**
* @template T
*/
class Box {
>Box : Box<T>
#value;
>#value : T
/** @param {T} initialValue */
constructor(initialValue) {
>initialValue : T
this.#value = initialValue;
>this.#value = initialValue : T
>this.#value : T
>this : this
>initialValue : T
}
/** @type {T} */
get value() {
>value : T
return this.#value;
>this.#value : T
>this : this
}
set value(value) {
>value : T
>value : T
this.#value = value;
>this.#value = value : T
>this.#value : T
>this : this
>value : T
}
}
new Box(3).value = 3;
>new Box(3).value = 3 : 3
>new Box(3).value : number
>new Box(3) : Box<number>
>Box : typeof Box
>3 : 3
>value : number
>3 : 3

View File

@ -0,0 +1,27 @@
// @target: esnext
module Generic {
class C<T> {
get y(): T {
return 1 as never;
}
set y(v) { }
}
var c = new C<number>();
c.y = c.y;
class Box<T> {
#value!: T;
get value() {
return this.#value;
}
set value(value) {
this.#value = value;
}
}
new Box<number>().value = 3;
}

View File

@ -0,0 +1,30 @@
// @target: esnext
// @lib: esnext
// @declaration: true
// @allowJs: true
// @checkJs: true
// @filename: genericSetterInClassTypeJsDoc.js
// @out: genericSetterInClassTypeJsDoc-out.js
/**
* @template T
*/
class Box {
#value;
/** @param {T} initialValue */
constructor(initialValue) {
this.#value = initialValue;
}
/** @type {T} */
get value() {
return this.#value;
}
set value(value) {
this.#value = value;
}
}
new Box(3).value = 3;