Fix serialisation of static class members in JS (#37780)
* Fix serialisation of static class members in JS Previously static class members would be treated the same way as expando namespace assignments to a class: ```ts class C { static get x() { return 1 } } C.y = 12 ``` This PR adds a syntactic check to the static/namespace filter that treats symbols whose valueDeclaration.parent is a class as statics. Fixes #37289 * fix messed-up indent * Extract function
This commit is contained in:
parent
20ecbb0f46
commit
eac073894b
|
@ -5971,7 +5971,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getNamespaceMembersForSerialization(symbol: Symbol) {
|
||||
return !symbol.exports ? [] : filter(arrayFrom((symbol.exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype")));
|
||||
return !symbol.exports ? [] : filter(arrayFrom(symbol.exports.values()), isNamespaceMember);
|
||||
}
|
||||
|
||||
function isTypeOnlyNamespace(symbol: Symbol) {
|
||||
|
@ -6103,7 +6103,7 @@ namespace ts {
|
|||
}
|
||||
// Module symbol emit will take care of module-y members, provided it has exports
|
||||
if (!(symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) && !!symbol.exports && !!symbol.exports.size)) {
|
||||
const props = filter(getPropertiesOfType(type), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype")));
|
||||
const props = filter(getPropertiesOfType(type), isNamespaceMember);
|
||||
serializeAsNamespaceDeclaration(props, localName, modifierFlags, /*suppressNewPrivateContext*/ true);
|
||||
}
|
||||
}
|
||||
|
@ -6159,6 +6159,10 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function isNamespaceMember(p: Symbol) {
|
||||
return !(p.flags & SymbolFlags.Prototype || p.escapedName === "prototype" || p.valueDeclaration && isClassLike(p.valueDeclaration.parent));
|
||||
}
|
||||
|
||||
function serializeAsClass(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) {
|
||||
const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
|
||||
const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context));
|
||||
|
@ -6195,12 +6199,9 @@ namespace ts {
|
|||
emptyArray;
|
||||
const publicProperties = flatMap<Symbol, ClassElement>(publicSymbolProps, p => serializePropertySymbolForClass(p, /*isStatic*/ false, baseTypes[0]));
|
||||
// Consider static members empty if symbol also has function or module meaning - function namespacey emit will handle statics
|
||||
const staticMembers = symbol.flags & (SymbolFlags.Function | SymbolFlags.ValueModule)
|
||||
? []
|
||||
: flatMap(filter(
|
||||
getPropertiesOfType(staticType),
|
||||
p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype"
|
||||
), p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
|
||||
const staticMembers = flatMap(
|
||||
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
|
||||
p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
|
||||
const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
|
||||
for (const c of constructors) {
|
||||
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
|
||||
|
@ -6363,7 +6364,7 @@ namespace ts {
|
|||
includePrivateSymbol(referenced || target);
|
||||
}
|
||||
|
||||
// We disable the context's symbol traker for the duration of this name serialization
|
||||
// We disable the context's symbol tracker for the duration of this name serialization
|
||||
// as, by virtue of being here, the name is required to print something, and we don't want to
|
||||
// issue a visibility error on it. Only anonymous classes that an alias points at _would_ issue
|
||||
// a visibility error here (as they're not visible within any scope), but we want to hoist them
|
||||
|
@ -6479,10 +6480,11 @@ namespace ts {
|
|||
// need to be merged namespace members
|
||||
return [];
|
||||
}
|
||||
if (p.flags & SymbolFlags.Prototype || (baseType && getPropertyOfType(baseType, p.escapedName)
|
||||
&& isReadonlySymbol(getPropertyOfType(baseType, p.escapedName)!) === isReadonlySymbol(p)
|
||||
&& (p.flags & SymbolFlags.Optional) === (getPropertyOfType(baseType, p.escapedName)!.flags & SymbolFlags.Optional)
|
||||
&& isTypeIdenticalTo(getTypeOfSymbol(p), getTypeOfPropertyOfType(baseType, p.escapedName)!))) {
|
||||
if (p.flags & SymbolFlags.Prototype ||
|
||||
(baseType && getPropertyOfType(baseType, p.escapedName)
|
||||
&& isReadonlySymbol(getPropertyOfType(baseType, p.escapedName)!) === isReadonlySymbol(p)
|
||||
&& (p.flags & SymbolFlags.Optional) === (getPropertyOfType(baseType, p.escapedName)!.flags & SymbolFlags.Optional)
|
||||
&& isTypeIdenticalTo(getTypeOfSymbol(p), getTypeOfPropertyOfType(baseType, p.escapedName)!))) {
|
||||
return [];
|
||||
}
|
||||
const flag = (modifierFlags & ~ModifierFlags.Async) | (isStatic ? ModifierFlags.Static : 0);
|
||||
|
|
74
tests/baselines/reference/jsDeclarationsClassStatic.js
Normal file
74
tests/baselines/reference/jsDeclarationsClassStatic.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
//// [source.js]
|
||||
class Handler {
|
||||
static get OPTIONS() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
process() {
|
||||
}
|
||||
}
|
||||
Handler.statische = function() { }
|
||||
const Strings = {
|
||||
a: "A",
|
||||
b: "B"
|
||||
}
|
||||
|
||||
module.exports = Handler;
|
||||
module.exports.Strings = Strings
|
||||
|
||||
/**
|
||||
* @typedef {Object} HandlerOptions
|
||||
* @property {String} name
|
||||
* Should be able to export a type alias at the same time.
|
||||
*/
|
||||
|
||||
|
||||
//// [source.js]
|
||||
var Handler = /** @class */ (function () {
|
||||
function Handler() {
|
||||
}
|
||||
Object.defineProperty(Handler, "OPTIONS", {
|
||||
get: function () {
|
||||
return 1;
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Handler.prototype.process = function () {
|
||||
};
|
||||
return Handler;
|
||||
}());
|
||||
Handler.statische = function () { };
|
||||
var Strings = {
|
||||
a: "A",
|
||||
b: "B"
|
||||
};
|
||||
module.exports = Handler;
|
||||
module.exports.Strings = Strings;
|
||||
/**
|
||||
* @typedef {Object} HandlerOptions
|
||||
* @property {String} name
|
||||
* Should be able to export a type alias at the same time.
|
||||
*/
|
||||
|
||||
|
||||
//// [source.d.ts]
|
||||
export = Handler;
|
||||
declare class Handler {
|
||||
static get OPTIONS(): number;
|
||||
process(): void;
|
||||
}
|
||||
declare namespace Handler {
|
||||
export { statische, Strings, HandlerOptions };
|
||||
}
|
||||
declare function statische(): void;
|
||||
declare namespace Strings {
|
||||
export const a: string;
|
||||
export const b: string;
|
||||
}
|
||||
type HandlerOptions = {
|
||||
/**
|
||||
* Should be able to export a type alias at the same time.
|
||||
*/
|
||||
name: String;
|
||||
};
|
49
tests/baselines/reference/jsDeclarationsClassStatic.symbols
Normal file
49
tests/baselines/reference/jsDeclarationsClassStatic.symbols
Normal file
|
@ -0,0 +1,49 @@
|
|||
=== tests/cases/conformance/jsdoc/declarations/source.js ===
|
||||
class Handler {
|
||||
>Handler : Symbol(Handler, Decl(source.js, 0, 0), Decl(source.js, 7, 1))
|
||||
|
||||
static get OPTIONS() {
|
||||
>OPTIONS : Symbol(Handler.OPTIONS, Decl(source.js, 0, 15))
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
process() {
|
||||
>process : Symbol(Handler.process, Decl(source.js, 3, 2))
|
||||
}
|
||||
}
|
||||
Handler.statische = function() { }
|
||||
>Handler.statische : Symbol(Handler.statische, Decl(source.js, 7, 1))
|
||||
>Handler : Symbol(Handler, Decl(source.js, 0, 0), Decl(source.js, 7, 1))
|
||||
>statische : Symbol(Handler.statische, Decl(source.js, 7, 1))
|
||||
|
||||
const Strings = {
|
||||
>Strings : Symbol(Strings, Decl(source.js, 9, 5))
|
||||
|
||||
a: "A",
|
||||
>a : Symbol(a, Decl(source.js, 9, 17))
|
||||
|
||||
b: "B"
|
||||
>b : Symbol(b, Decl(source.js, 10, 11))
|
||||
}
|
||||
|
||||
module.exports = Handler;
|
||||
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/source", Decl(source.js, 0, 0))
|
||||
>module : Symbol(export=, Decl(source.js, 12, 1))
|
||||
>exports : Symbol(export=, Decl(source.js, 12, 1))
|
||||
>Handler : Symbol(Handler, Decl(source.js, 0, 0), Decl(source.js, 7, 1))
|
||||
|
||||
module.exports.Strings = Strings
|
||||
>module.exports.Strings : Symbol(Strings)
|
||||
>module.exports : Symbol(Strings, Decl(source.js, 14, 25))
|
||||
>module : Symbol(module, Decl(source.js, 12, 1))
|
||||
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/source", Decl(source.js, 0, 0))
|
||||
>Strings : Symbol(Strings, Decl(source.js, 14, 25))
|
||||
>Strings : Symbol(Strings, Decl(source.js, 9, 5))
|
||||
|
||||
/**
|
||||
* @typedef {Object} HandlerOptions
|
||||
* @property {String} name
|
||||
* Should be able to export a type alias at the same time.
|
||||
*/
|
||||
|
57
tests/baselines/reference/jsDeclarationsClassStatic.types
Normal file
57
tests/baselines/reference/jsDeclarationsClassStatic.types
Normal file
|
@ -0,0 +1,57 @@
|
|||
=== tests/cases/conformance/jsdoc/declarations/source.js ===
|
||||
class Handler {
|
||||
>Handler : Handler
|
||||
|
||||
static get OPTIONS() {
|
||||
>OPTIONS : number
|
||||
|
||||
return 1;
|
||||
>1 : 1
|
||||
}
|
||||
|
||||
process() {
|
||||
>process : () => void
|
||||
}
|
||||
}
|
||||
Handler.statische = function() { }
|
||||
>Handler.statische = function() { } : () => void
|
||||
>Handler.statische : () => void
|
||||
>Handler : typeof Handler
|
||||
>statische : () => void
|
||||
>function() { } : () => void
|
||||
|
||||
const Strings = {
|
||||
>Strings : { a: string; b: string; }
|
||||
>{ a: "A", b: "B"} : { a: string; b: string; }
|
||||
|
||||
a: "A",
|
||||
>a : string
|
||||
>"A" : "A"
|
||||
|
||||
b: "B"
|
||||
>b : string
|
||||
>"B" : "B"
|
||||
}
|
||||
|
||||
module.exports = Handler;
|
||||
>module.exports = Handler : typeof Handler
|
||||
>module.exports : typeof Handler
|
||||
>module : { "\"tests/cases/conformance/jsdoc/declarations/source\"": typeof Handler; }
|
||||
>exports : typeof Handler
|
||||
>Handler : typeof Handler
|
||||
|
||||
module.exports.Strings = Strings
|
||||
>module.exports.Strings = Strings : { a: string; b: string; }
|
||||
>module.exports.Strings : { a: string; b: string; }
|
||||
>module.exports : typeof Handler
|
||||
>module : { "\"tests/cases/conformance/jsdoc/declarations/source\"": typeof Handler; }
|
||||
>exports : typeof Handler
|
||||
>Strings : { a: string; b: string; }
|
||||
>Strings : { a: string; b: string; }
|
||||
|
||||
/**
|
||||
* @typedef {Object} HandlerOptions
|
||||
* @property {String} name
|
||||
* Should be able to export a type alias at the same time.
|
||||
*/
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @target: es5
|
||||
// @outDir: ./out
|
||||
// @declaration: true
|
||||
// @filename: source.js
|
||||
class Handler {
|
||||
static get OPTIONS() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
process() {
|
||||
}
|
||||
}
|
||||
Handler.statische = function() { }
|
||||
const Strings = {
|
||||
a: "A",
|
||||
b: "B"
|
||||
}
|
||||
|
||||
module.exports = Handler;
|
||||
module.exports.Strings = Strings
|
||||
|
||||
/**
|
||||
* @typedef {Object} HandlerOptions
|
||||
* @property {String} name
|
||||
* Should be able to export a type alias at the same time.
|
||||
*/
|
Loading…
Reference in a new issue