Fixed declaration emit of object literals withs divergent accessors (#55442)

This commit is contained in:
Mateusz Burzyński
2023-10-06 01:40:45 +02:00
committed by GitHub
parent 5001433b10
commit 7377f5cb9d
14 changed files with 566 additions and 19 deletions

View File

@@ -7292,6 +7292,30 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const propertyName = getPropertyNameNodeForSymbol(propertySymbol, context);
context.enclosingDeclaration = saveEnclosingDeclaration;
context.approximateLength += symbolName(propertySymbol).length + 1;
if (propertySymbol.flags & SymbolFlags.Accessor) {
const writeType = getWriteTypeOfSymbol(propertySymbol);
if (propertyType !== writeType) {
const getterDeclaration = getDeclarationOfKind<GetAccessorDeclaration>(propertySymbol, SyntaxKind.GetAccessor)!;
const getterSignature = getSignatureFromDeclaration(getterDeclaration);
typeElements.push(
setCommentRange(
signatureToSignatureDeclarationHelper(getterSignature, SyntaxKind.GetAccessor, context, { name: propertyName }) as GetAccessorDeclaration,
getterDeclaration,
),
);
const setterDeclaration = getDeclarationOfKind<SetAccessorDeclaration>(propertySymbol, SyntaxKind.SetAccessor)!;
const setterSignature = getSignatureFromDeclaration(setterDeclaration);
typeElements.push(
setCommentRange(
signatureToSignatureDeclarationHelper(setterSignature, SyntaxKind.SetAccessor, context, { name: propertyName }) as SetAccessorDeclaration,
setterDeclaration,
),
);
return;
}
}
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined;
if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length && !isReadonlySymbol(propertySymbol)) {
const signatures = getSignaturesOfType(filterType(propertyType, t => !(t.flags & TypeFlags.Undefined)), SignatureKind.Call);
@@ -7332,9 +7356,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
typeElements.push(preserveCommentsOn(propertySignature));
function preserveCommentsOn<T extends Node>(node: T) {
if (some(propertySymbol.declarations, d => d.kind === SyntaxKind.JSDocPropertyTag)) {
const d = propertySymbol.declarations?.find(d => d.kind === SyntaxKind.JSDocPropertyTag)! as JSDocPropertyTag;
const commentText = getTextOfJSDocComment(d.comment);
const jsdocPropertyTag = propertySymbol.declarations?.find((d): d is JSDocPropertyTag => d.kind === SyntaxKind.JSDocPropertyTag);
if (jsdocPropertyTag) {
const commentText = getTextOfJSDocComment(jsdocPropertyTag.comment);
if (commentText) {
setSyntheticLeadingComments(node, [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }]);
}
@@ -9798,7 +9822,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
!(typeToSerialize.symbol && some(typeToSerialize.symbol.declarations, d => getSourceFileOfNode(d) !== ctxSrc)) &&
!some(getPropertiesOfType(typeToSerialize), p => isLateBoundName(p.escapedName)) &&
!some(getPropertiesOfType(typeToSerialize), p => some(p.declarations, d => getSourceFileOfNode(d) !== ctxSrc)) &&
every(getPropertiesOfType(typeToSerialize), p => isIdentifierText(symbolName(p), languageVersion));
every(getPropertiesOfType(typeToSerialize), p => {
if (!isIdentifierText(symbolName(p), languageVersion)) {
return false;
}
if (!(p.flags & SymbolFlags.Accessor)) {
return true;
}
return getNonMissingTypeOfSymbol(p) === getWriteTypeOfSymbol(p);
});
}
function makeSerializePropertySymbol<T extends Node>(

View File

@@ -0,0 +1,57 @@
//// [tests/cases/compiler/declarationEmitObjectLiteralAccessors1.ts] ////
//// [declarationEmitObjectLiteralAccessors1.ts]
// same type accessors
export const obj1 = {
/** my awesome getter (first in source order) */
get x(): string {
return "";
},
/** my awesome setter (second in source order) */
set x(a: string) {},
};
// divergent accessors
export const obj2 = {
/** my awesome getter */
get x(): string {
return "";
},
/** my awesome setter */
set x(a: number) {},
};
export const obj3 = {
/** my awesome getter */
get x(): string {
return "";
},
};
export const obj4 = {
/** my awesome setter */
set x(a: number) {},
};
//// [declarationEmitObjectLiteralAccessors1.d.ts]
export declare const obj1: {
/** my awesome getter (first in source order) */
x: string;
};
export declare const obj2: {
/** my awesome getter */
get x(): string;
/** my awesome setter */
set x(a: number);
};
export declare const obj3: {
/** my awesome getter */
readonly x: string;
};
export declare const obj4: {
/** my awesome setter */
x: number;
};

View File

@@ -0,0 +1,58 @@
//// [tests/cases/compiler/declarationEmitObjectLiteralAccessors1.ts] ////
=== declarationEmitObjectLiteralAccessors1.ts ===
// same type accessors
export const obj1 = {
>obj1 : Symbol(obj1, Decl(declarationEmitObjectLiteralAccessors1.ts, 1, 12))
/** my awesome getter (first in source order) */
get x(): string {
>x : Symbol(x, Decl(declarationEmitObjectLiteralAccessors1.ts, 1, 21), Decl(declarationEmitObjectLiteralAccessors1.ts, 5, 4))
return "";
},
/** my awesome setter (second in source order) */
set x(a: string) {},
>x : Symbol(x, Decl(declarationEmitObjectLiteralAccessors1.ts, 1, 21), Decl(declarationEmitObjectLiteralAccessors1.ts, 5, 4))
>a : Symbol(a, Decl(declarationEmitObjectLiteralAccessors1.ts, 7, 8))
};
// divergent accessors
export const obj2 = {
>obj2 : Symbol(obj2, Decl(declarationEmitObjectLiteralAccessors1.ts, 11, 12))
/** my awesome getter */
get x(): string {
>x : Symbol(x, Decl(declarationEmitObjectLiteralAccessors1.ts, 11, 21), Decl(declarationEmitObjectLiteralAccessors1.ts, 15, 4))
return "";
},
/** my awesome setter */
set x(a: number) {},
>x : Symbol(x, Decl(declarationEmitObjectLiteralAccessors1.ts, 11, 21), Decl(declarationEmitObjectLiteralAccessors1.ts, 15, 4))
>a : Symbol(a, Decl(declarationEmitObjectLiteralAccessors1.ts, 17, 8))
};
export const obj3 = {
>obj3 : Symbol(obj3, Decl(declarationEmitObjectLiteralAccessors1.ts, 20, 12))
/** my awesome getter */
get x(): string {
>x : Symbol(x, Decl(declarationEmitObjectLiteralAccessors1.ts, 20, 21))
return "";
},
};
export const obj4 = {
>obj4 : Symbol(obj4, Decl(declarationEmitObjectLiteralAccessors1.ts, 27, 12))
/** my awesome setter */
set x(a: number) {},
>x : Symbol(x, Decl(declarationEmitObjectLiteralAccessors1.ts, 27, 21))
>a : Symbol(a, Decl(declarationEmitObjectLiteralAccessors1.ts, 29, 8))
};

View File

@@ -0,0 +1,68 @@
//// [tests/cases/compiler/declarationEmitObjectLiteralAccessors1.ts] ////
=== declarationEmitObjectLiteralAccessors1.ts ===
// same type accessors
export const obj1 = {
>obj1 : { x: string; }
>{ /** my awesome getter (first in source order) */ get x(): string { return ""; }, /** my awesome setter (second in source order) */ set x(a: string) {},} : { x: string; }
/** my awesome getter (first in source order) */
get x(): string {
>x : string
return "";
>"" : ""
},
/** my awesome setter (second in source order) */
set x(a: string) {},
>x : string
>a : string
};
// divergent accessors
export const obj2 = {
>obj2 : { get x(): string; set x(a: number); }
>{ /** my awesome getter */ get x(): string { return ""; }, /** my awesome setter */ set x(a: number) {},} : { get x(): string; set x(a: number); }
/** my awesome getter */
get x(): string {
>x : string
return "";
>"" : ""
},
/** my awesome setter */
set x(a: number) {},
>x : string
>a : number
};
export const obj3 = {
>obj3 : { readonly x: string; }
>{ /** my awesome getter */ get x(): string { return ""; },} : { readonly x: string; }
/** my awesome getter */
get x(): string {
>x : string
return "";
>"" : ""
},
};
export const obj4 = {
>obj4 : { x: number; }
>{ /** my awesome setter */ set x(a: number) {},} : { x: number; }
/** my awesome setter */
set x(a: number) {},
>x : number
>a : number
};

View File

@@ -0,0 +1,80 @@
//// [tests/cases/compiler/declarationEmitObjectLiteralAccessorsJs1.ts] ////
//// [index.js]
// same type accessors
export const obj1 = {
/**
* my awesome getter (first in source order)
* @returns {string}
*/
get x() {
return "";
},
/**
* my awesome setter (second in source order)
* @param {string} a
*/
set x(a) {},
};
// divergent accessors
export const obj2 = {
/**
* my awesome getter
* @returns {string}
*/
get x() {
return "";
},
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
};
export const obj3 = {
/**
* my awesome getter
* @returns {string}
*/
get x() {
return "";
},
};
export const obj4 = {
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
};
//// [index.d.ts]
export namespace obj1 {
let x: string;
}
export const obj2: {
/**
* my awesome getter
* @returns {string}
*/
get x(): string;
/**
* my awesome setter
* @param {number} a
*/
set x(a: number);
};
export namespace obj3 {
const x_1: string;
export { x_1 as x };
}
export namespace obj4 {
let x_2: number;
export { x_2 as x };
}

View File

@@ -0,0 +1,76 @@
//// [tests/cases/compiler/declarationEmitObjectLiteralAccessorsJs1.ts] ////
=== index.js ===
// same type accessors
export const obj1 = {
>obj1 : Symbol(obj1, Decl(index.js, 1, 12))
/**
* my awesome getter (first in source order)
* @returns {string}
*/
get x() {
>x : Symbol(x, Decl(index.js, 1, 21), Decl(index.js, 8, 4))
return "";
},
/**
* my awesome setter (second in source order)
* @param {string} a
*/
set x(a) {},
>x : Symbol(x, Decl(index.js, 1, 21), Decl(index.js, 8, 4))
>a : Symbol(a, Decl(index.js, 13, 8))
};
// divergent accessors
export const obj2 = {
>obj2 : Symbol(obj2, Decl(index.js, 17, 12))
/**
* my awesome getter
* @returns {string}
*/
get x() {
>x : Symbol(x, Decl(index.js, 17, 21), Decl(index.js, 24, 4))
return "";
},
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
>x : Symbol(x, Decl(index.js, 17, 21), Decl(index.js, 24, 4))
>a : Symbol(a, Decl(index.js, 29, 8))
};
export const obj3 = {
>obj3 : Symbol(obj3, Decl(index.js, 32, 12))
/**
* my awesome getter
* @returns {string}
*/
get x() {
>x : Symbol(x, Decl(index.js, 32, 21))
return "";
},
};
export const obj4 = {
>obj4 : Symbol(obj4, Decl(index.js, 42, 12))
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
>x : Symbol(x, Decl(index.js, 42, 21))
>a : Symbol(a, Decl(index.js, 47, 8))
};

View File

@@ -0,0 +1,86 @@
//// [tests/cases/compiler/declarationEmitObjectLiteralAccessorsJs1.ts] ////
=== index.js ===
// same type accessors
export const obj1 = {
>obj1 : { x: string; }
>{ /** * my awesome getter (first in source order) * @returns {string} */ get x() { return ""; }, /** * my awesome setter (second in source order) * @param {string} a */ set x(a) {},} : { x: string; }
/**
* my awesome getter (first in source order)
* @returns {string}
*/
get x() {
>x : string
return "";
>"" : ""
},
/**
* my awesome setter (second in source order)
* @param {string} a
*/
set x(a) {},
>x : string
>a : string
};
// divergent accessors
export const obj2 = {
>obj2 : { get x(): string; set x(a: number); }
>{ /** * my awesome getter * @returns {string} */ get x() { return ""; }, /** * my awesome setter * @param {number} a */ set x(a) {},} : { get x(): string; set x(a: number); }
/**
* my awesome getter
* @returns {string}
*/
get x() {
>x : string
return "";
>"" : ""
},
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
>x : string
>a : number
};
export const obj3 = {
>obj3 : { readonly x: string; }
>{ /** * my awesome getter * @returns {string} */ get x() { return ""; },} : { readonly x: string; }
/**
* my awesome getter
* @returns {string}
*/
get x() {
>x : string
return "";
>"" : ""
},
};
export const obj4 = {
>obj4 : { x: number; }
>{ /** * my awesome setter * @param {number} a */ set x(a) {},} : { x: number; }
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
>x : number
>a : number
};

View File

@@ -27,7 +27,7 @@ if (weirdoBox.done) {
weirdoBox.value;
>weirdoBox.value : number
>weirdoBox : { done: true; value: number; }
>weirdoBox : { get done(): true; set done(v: T | null); value: number; }
>value : number
}
@@ -64,7 +64,7 @@ if (weirdoBox2.done) {
weirdoBox2.value;
>weirdoBox2.value : string | number
>weirdoBox2 : { done: true; value: string; } | { done: true; value: number; }
>weirdoBox2 : { get done(): true; set done(v: T | null); value: string; } | { get done(): true; set done(v: T | null | undefined); value: number; }
>value : string | number
}

View File

@@ -33,7 +33,7 @@
{
type T_HasGetSet = {
>T_HasGetSet : { foo: number; }
>T_HasGetSet : { get foo(): number; set foo(v: number | string); }
get foo(): number;
>foo : number
@@ -44,20 +44,20 @@
}
const t_hgs: T_HasGetSet = null as any;
>t_hgs : { foo: number; }
>t_hgs : { get foo(): number; set foo(v: string | number); }
>null as any : any
t_hgs.foo = "32";
>t_hgs.foo = "32" : "32"
>t_hgs.foo : string | number
>t_hgs : { foo: number; }
>t_hgs : { get foo(): number; set foo(v: string | number); }
>foo : string | number
>"32" : "32"
let r_t_hgs_foo: number = t_hgs.foo;
>r_t_hgs_foo : number
>t_hgs.foo : number
>t_hgs : { foo: number; }
>t_hgs : { get foo(): number; set foo(v: string | number); }
>foo : number
}

View File

@@ -50,7 +50,7 @@ interface Test2 {
}
type Test3 = {
>Test3 : { foo: string; bar: string | number; }
>Test3 : { get foo(): string; set foo(s: string | number); get bar(): string | number; set bar(s: string | number | boolean); }
get foo(): string;
>foo : string

View File

@@ -53,8 +53,8 @@ interface I1 {
>value : string
}
const o1 = {
>o1 : { x: number; }
>{ get x(): number { return 0; }, set x(value: Fail<string>) {}} : { x: number; }
>o1 : { get x(): number; set x(value: Fail<string>); }
>{ get x(): number { return 0; }, set x(value: Fail<string>) {}} : { get x(): number; set x(value: Fail<string>); }
get x(): number { return 0; },
>x : number
@@ -68,8 +68,8 @@ const o1 = {
// A setter annotation still implies the getter return type.
const o2 = {
>o2 : { p1: string; p2: number; }
>{ get p1() { return 0; }, // error - no annotation means type is implied from the setter annotation set p1(value: string) {}, get p2(): number { return 0; }, // ok - explicit annotation set p2(value: string) {},} : { p1: string; p2: number; }
>o2 : { p1: string; get p2(): number; set p2(value: string); }
>{ get p1() { return 0; }, // error - no annotation means type is implied from the setter annotation set p1(value: string) {}, get p2(): number { return 0; }, // ok - explicit annotation set p2(value: string) {},} : { p1: string; get p2(): number; set p2(value: string); }
get p1() { return 0; }, // error - no annotation means type is implied from the setter annotation
>p1 : string

View File

@@ -297,8 +297,8 @@ var f17 = { a: 0, get b() { return 1; }, get a() { return 0; } };
// Get and set accessor with mismatched type annotations (only g2 is an error after #43662 implemented)
var g1 = { get a(): number { return 4; }, set a(n: string) { } };
>g1 : { a: number; }
>{ get a(): number { return 4; }, set a(n: string) { } } : { a: number; }
>g1 : { get a(): number; set a(n: string); }
>{ get a(): number { return 4; }, set a(n: string) { } } : { get a(): number; set a(n: string); }
>a : number
>4 : 4
>a : number
@@ -313,8 +313,8 @@ var g2 = { get a() { return 4; }, set a(n: string) { } };
>n : string
var g3 = { get a(): number { return undefined; }, set a(n: string) { } };
>g3 : { a: number; }
>{ get a(): number { return undefined; }, set a(n: string) { } } : { a: number; }
>g3 : { get a(): number; set a(n: string); }
>{ get a(): number { return undefined; }, set a(n: string) { } } : { get a(): number; set a(n: string); }
>a : number
>undefined : undefined
>a : number

View File

@@ -0,0 +1,35 @@
// @strict: true
// @declaration: true
// @emitDeclarationOnly: true
// same type accessors
export const obj1 = {
/** my awesome getter (first in source order) */
get x(): string {
return "";
},
/** my awesome setter (second in source order) */
set x(a: string) {},
};
// divergent accessors
export const obj2 = {
/** my awesome getter */
get x(): string {
return "";
},
/** my awesome setter */
set x(a: number) {},
};
export const obj3 = {
/** my awesome getter */
get x(): string {
return "";
},
};
export const obj4 = {
/** my awesome setter */
set x(a: number) {},
};

View File

@@ -0,0 +1,55 @@
// @strict: true
// @checkJs: true
// @declaration: true
// @emitDeclarationOnly: true
// @filename: index.js
// same type accessors
export const obj1 = {
/**
* my awesome getter (first in source order)
* @returns {string}
*/
get x() {
return "";
},
/**
* my awesome setter (second in source order)
* @param {string} a
*/
set x(a) {},
};
// divergent accessors
export const obj2 = {
/**
* my awesome getter
* @returns {string}
*/
get x() {
return "";
},
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
};
export const obj3 = {
/**
* my awesome getter
* @returns {string}
*/
get x() {
return "";
},
};
export const obj4 = {
/**
* my awesome setter
* @param {number} a
*/
set x(a) {},
};