mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Decorators normative changes (#52582)
This commit is contained in:
+27
-1
@@ -46445,6 +46445,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
let lastStatic: Node | undefined, lastDeclare: Node | undefined, lastAsync: Node | undefined, lastOverride: Node | undefined, firstDecorator: Decorator | undefined;
|
||||
let flags = ModifierFlags.None;
|
||||
let sawExportBeforeDecorators = false;
|
||||
// We parse decorators and modifiers in four contiguous chunks:
|
||||
// [...leadingDecorators, ...leadingModifiers, ...trailingDecorators, ...trailingModifiers]. It is an error to
|
||||
// have both leading and trailing decorators.
|
||||
let hasLeadingDecorators = false;
|
||||
for (const modifier of (node as HasModifiers).modifiers!) {
|
||||
if (isDecorator(modifier)) {
|
||||
if (!nodeCanBeDecorated(legacyDecorators, node, node.parent, node.parent.parent)) {
|
||||
@@ -46461,13 +46465,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return grammarErrorOnFirstToken(node, Diagnostics.Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name);
|
||||
}
|
||||
}
|
||||
|
||||
// if we've seen any modifiers aside from `export`, `default`, or another decorator, then this is an invalid position
|
||||
if (flags & ~(ModifierFlags.ExportDefault | ModifierFlags.Decorator)) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics.Decorators_are_not_valid_here);
|
||||
}
|
||||
|
||||
// if we've already seen leading decorators and leading modifiers, then trailing decorators are an invalid position
|
||||
if (hasLeadingDecorators && flags & ModifierFlags.Modifier) {
|
||||
Debug.assertIsDefined(firstDecorator);
|
||||
const sourceFile = getSourceFileOfNode(modifier);
|
||||
if (!hasParseDiagnostics(sourceFile)) {
|
||||
addRelatedInfo(
|
||||
error(modifier, Diagnostics.Decorators_may_not_appear_after_export_or_export_default_if_they_also_appear_before_export),
|
||||
createDiagnosticForNode(firstDecorator, Diagnostics.Decorator_used_before_export_here));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
flags |= ModifierFlags.Decorator;
|
||||
if (flags & ModifierFlags.Export) {
|
||||
|
||||
// if we have not yet seen a modifier, then these are leading decorators
|
||||
if (!(flags & ModifierFlags.Modifier)) {
|
||||
hasLeadingDecorators = true;
|
||||
}
|
||||
else if (flags & ModifierFlags.Export) {
|
||||
sawExportBeforeDecorators = true;
|
||||
}
|
||||
|
||||
firstDecorator ??= modifier;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1589,6 +1589,10 @@
|
||||
"category": "Error",
|
||||
"code": 1485
|
||||
},
|
||||
"Decorator used before 'export' here.": {
|
||||
"category": "Error",
|
||||
"code": 1486
|
||||
},
|
||||
|
||||
"The types of '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
@@ -6560,7 +6564,7 @@
|
||||
"category": "Error",
|
||||
"code": 8037
|
||||
},
|
||||
"Decorators must come after 'export' or 'export default' in JavaScript files.": {
|
||||
"Decorators may not appear after 'export' or 'export default' if they also appear before 'export'.": {
|
||||
"category": "Error",
|
||||
"code": 8038
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
isComputedPropertyName,
|
||||
isIdentifier,
|
||||
memoize,
|
||||
ObjectLiteralElementLike,
|
||||
PrivateIdentifier,
|
||||
ScriptTarget,
|
||||
setEmitFlags,
|
||||
@@ -250,155 +251,97 @@ export function createEmitHelperFactory(context: TransformationContext): EmitHel
|
||||
]);
|
||||
}
|
||||
|
||||
// Per https://github.com/tc39/proposal-decorators/issues/494, we may need to change the emit for the `access` object
|
||||
// so that it does not need to be used via `.call`. The following two sections represent the options presented in
|
||||
// tc39/proposal-decorators#494
|
||||
//
|
||||
// === Current approach (`access.get.call(obj)`, `access.set.call(obj, value)`) ===
|
||||
//
|
||||
// function createESDecorateClassElementAccessGetMethod(elementName: ESDecorateName) {
|
||||
// const accessor = elementName.computed ?
|
||||
// factory.createElementAccessExpression(factory.createThis(), elementName.name) :
|
||||
// factory.createPropertyAccessExpression(factory.createThis(), elementName.name);
|
||||
//
|
||||
// return factory.createMethodDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*asteriskToken*/ undefined,
|
||||
// "get",
|
||||
// /*questionToken*/ undefined,
|
||||
// /*typeParameters*/ undefined,
|
||||
// [],
|
||||
// /*type*/ undefined,
|
||||
// factory.createBlock([factory.createReturnStatement(accessor)])
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// function createESDecorateClassElementAccessSetMethod(elementName: ESDecorateName) {
|
||||
// const accessor = elementName.computed ?
|
||||
// factory.createElementAccessExpression(factory.createThis(), elementName.name) :
|
||||
// factory.createPropertyAccessExpression(factory.createThis(), elementName.name);
|
||||
//
|
||||
// return factory.createMethodDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*asteriskToken*/ undefined,
|
||||
// "set",
|
||||
// /*questionToken*/ undefined,
|
||||
// /*typeParameters*/ undefined,
|
||||
// [factory.createParameterDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*dotDotDotToken*/ undefined,
|
||||
// factory.createIdentifier("value")
|
||||
// )],
|
||||
// /*type*/ undefined,
|
||||
// factory.createBlock([
|
||||
// factory.createExpressionStatement(
|
||||
// factory.createAssignment(
|
||||
// accessor,
|
||||
// factory.createIdentifier("value")
|
||||
// )
|
||||
// )
|
||||
// ])
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// function createESDecorateClassElementAccessObject(name: ESDecorateName, access: ESDecorateClassElementAccess) {
|
||||
// const properties: ObjectLiteralElementLike[] = [];
|
||||
// if (access.get) properties.push(createESDecorateClassElementAccessGetMethod(name));
|
||||
// if (access.set) properties.push(createESDecorateClassElementAccessSetMethod(name));
|
||||
// return factory.createObjectLiteralExpression(properties);
|
||||
// }
|
||||
//
|
||||
// === Suggested approach (`access.get(obj)`, `access.set(obj, value)`, `access.has(obj)`) ===
|
||||
//
|
||||
// function createESDecorateClassElementAccessGetMethod(elementName: ESDecorateName) {
|
||||
// const accessor = elementName.computed ?
|
||||
// factory.createElementAccessExpression(factory.createIdentifier("obj"), elementName.name) :
|
||||
// factory.createPropertyAccessExpression(factory.createIdentifier("obj"), elementName.name);
|
||||
//
|
||||
// return factory.createMethodDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*asteriskToken*/ undefined,
|
||||
// "get",
|
||||
// /*questionToken*/ undefined,
|
||||
// /*typeParameters*/ undefined,
|
||||
// [factory.createParameterDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*dotDotDotToken*/ undefined,
|
||||
// factory.createIdentifier("obj")
|
||||
// )],
|
||||
// /*type*/ undefined,
|
||||
// factory.createBlock([factory.createReturnStatement(accessor)])
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// function createESDecorateClassElementAccessSetMethod(elementName: ESDecorateName) {
|
||||
// const accessor = elementName.computed ?
|
||||
// factory.createElementAccessExpression(factory.createIdentifier("obj"), elementName.name) :
|
||||
// factory.createPropertyAccessExpression(factory.createIdentifier("obj"), elementName.name);
|
||||
//
|
||||
// return factory.createMethodDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*asteriskToken*/ undefined,
|
||||
// "set",
|
||||
// /*questionToken*/ undefined,
|
||||
// /*typeParameters*/ undefined,
|
||||
// [factory.createParameterDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*dotDotDotToken*/ undefined,
|
||||
// factory.createIdentifier("obj")
|
||||
// ),
|
||||
// factory.createParameterDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*dotDotDotToken*/ undefined,
|
||||
// factory.createIdentifier("value")
|
||||
// )],
|
||||
// /*type*/ undefined,
|
||||
// factory.createBlock([
|
||||
// factory.createExpressionStatement(
|
||||
// factory.createAssignment(
|
||||
// accessor,
|
||||
// factory.createIdentifier("value")
|
||||
// )
|
||||
// )
|
||||
// ])
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// function createESDecorateClassElementAccessHasMethod(elementName: ESDecorateName) {
|
||||
// const propertyName =
|
||||
// elementName.computed ? elementName.name :
|
||||
// isIdentifier(elementName.name) ? factory.createStringLiteralFromNode(elementName.name) :
|
||||
// elementName.name;
|
||||
//
|
||||
// return factory.createMethodDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*asteriskToken*/ undefined,
|
||||
// "has",
|
||||
// /*questionToken*/ undefined,
|
||||
// /*typeParameters*/ undefined,
|
||||
// [factory.createParameterDeclaration(
|
||||
// /*modifiers*/ undefined,
|
||||
// /*dotDotDotToken*/ undefined,
|
||||
// factory.createIdentifier("obj")
|
||||
// )],
|
||||
// /*type*/ undefined,
|
||||
// factory.createBlock([factory.createReturnStatement(
|
||||
// factory.createBinaryExpression(
|
||||
// propertyName,
|
||||
// SyntaxKind.InKeyword,
|
||||
// factory.createIdentifier("obj")
|
||||
// )
|
||||
// )])
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// function createESDecorateClassElementAccessObject(name: ESDecorateName, access: ESDecorateClassElementAccess) {
|
||||
// const properties: ObjectLiteralElementLike[] = [];
|
||||
// if (access.get) properties.push(createESDecorateClassElementAccessGetMethod(name));
|
||||
// if (access.set) properties.push(createESDecorateClassElementAccessSetMethod(name));
|
||||
// property.push(createESDecorateClassElementAccessHasMethod(name));
|
||||
// return factory.createObjectLiteralExpression(properties);
|
||||
// }
|
||||
|
||||
function createESDecorateClassElementAccessGetMethod(elementName: ESDecorateName) {
|
||||
const accessor = elementName.computed ?
|
||||
factory.createElementAccessExpression(factory.createIdentifier("obj"), elementName.name) :
|
||||
factory.createPropertyAccessExpression(factory.createIdentifier("obj"), elementName.name);
|
||||
|
||||
return factory.createPropertyAssignment(
|
||||
"get",
|
||||
factory.createArrowFunction(
|
||||
/*modifiers*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[factory.createParameterDeclaration(
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
factory.createIdentifier("obj")
|
||||
)],
|
||||
/*type*/ undefined,
|
||||
/*equalsGreaterThanToken*/ undefined,
|
||||
accessor
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function createESDecorateClassElementAccessSetMethod(elementName: ESDecorateName) {
|
||||
const accessor = elementName.computed ?
|
||||
factory.createElementAccessExpression(factory.createIdentifier("obj"), elementName.name) :
|
||||
factory.createPropertyAccessExpression(factory.createIdentifier("obj"), elementName.name);
|
||||
|
||||
return factory.createPropertyAssignment(
|
||||
"set",
|
||||
factory.createArrowFunction(
|
||||
/*modifiers*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[factory.createParameterDeclaration(
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
factory.createIdentifier("obj")
|
||||
),
|
||||
factory.createParameterDeclaration(
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
factory.createIdentifier("value")
|
||||
)],
|
||||
/*type*/ undefined,
|
||||
/*equalsGreaterThanToken*/ undefined,
|
||||
factory.createBlock([
|
||||
factory.createExpressionStatement(
|
||||
factory.createAssignment(
|
||||
accessor,
|
||||
factory.createIdentifier("value")
|
||||
)
|
||||
)
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function createESDecorateClassElementAccessHasMethod(elementName: ESDecorateName) {
|
||||
const propertyName =
|
||||
elementName.computed ? elementName.name :
|
||||
isIdentifier(elementName.name) ? factory.createStringLiteralFromNode(elementName.name) :
|
||||
elementName.name;
|
||||
|
||||
return factory.createPropertyAssignment(
|
||||
"has",
|
||||
factory.createArrowFunction(
|
||||
/*modifiers*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[factory.createParameterDeclaration(
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
factory.createIdentifier("obj")
|
||||
)],
|
||||
/*type*/ undefined,
|
||||
/*equalsGreaterThanToken*/ undefined,
|
||||
factory.createBinaryExpression(
|
||||
propertyName,
|
||||
SyntaxKind.InKeyword,
|
||||
factory.createIdentifier("obj")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function createESDecorateClassElementAccessObject(name: ESDecorateName, access: ESDecorateClassElementAccess) {
|
||||
const properties: ObjectLiteralElementLike[] = [];
|
||||
properties.push(createESDecorateClassElementAccessHasMethod(name));
|
||||
if (access.get) properties.push(createESDecorateClassElementAccessGetMethod(name));
|
||||
if (access.set) properties.push(createESDecorateClassElementAccessSetMethod(name));
|
||||
return factory.createObjectLiteralExpression(properties);
|
||||
}
|
||||
|
||||
function createESDecorateClassElementContextObject(contextIn: ESDecorateClassElementContext) {
|
||||
return factory.createObjectLiteralExpression([
|
||||
@@ -406,9 +349,7 @@ export function createEmitHelperFactory(context: TransformationContext): EmitHel
|
||||
factory.createPropertyAssignment(factory.createIdentifier("name"), contextIn.name.computed ? contextIn.name.name : factory.createStringLiteralFromNode(contextIn.name.name)),
|
||||
factory.createPropertyAssignment(factory.createIdentifier("static"), contextIn.static ? factory.createTrue() : factory.createFalse()),
|
||||
factory.createPropertyAssignment(factory.createIdentifier("private"), contextIn.private ? factory.createTrue() : factory.createFalse()),
|
||||
|
||||
// Disabled, pending resolution of https://github.com/tc39/proposal-decorators/issues/494
|
||||
// factory.createPropertyAssignment(factory.createIdentifier("access"), createESDecorateClassElementAccessObject(contextIn.name, contextIn.access))
|
||||
factory.createPropertyAssignment(factory.createIdentifier("access"), createESDecorateClassElementAccessObject(contextIn.name, contextIn.access))
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
+24
-8
@@ -7710,20 +7710,36 @@ namespace Parser {
|
||||
function parseModifiers(allowDecorators: boolean, permitConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean): NodeArray<ModifierLike> | undefined {
|
||||
const pos = getNodePos();
|
||||
let list: ModifierLike[] | undefined;
|
||||
let modifier, hasSeenStaticModifier = false;
|
||||
while (modifier = tryParseModifier(hasSeenStaticModifier, permitConstAsModifier, stopOnStartOfClassStaticBlock)) {
|
||||
if (modifier.kind === SyntaxKind.StaticKeyword) hasSeenStaticModifier = true;
|
||||
list = append(list, modifier);
|
||||
}
|
||||
let decorator, modifier, hasSeenStaticModifier = false, hasLeadingModifier = false, hasTrailingDecorator = false;
|
||||
|
||||
// Decorators should be contiguous in a list of modifiers (i.e., `[...leadingModifiers, ...decorators, ...trailingModifiers]`).
|
||||
// The leading modifiers *should* only contain `export` and `default` when decorators are present, but we'll handle errors for any other leading modifiers in the checker.
|
||||
// Decorators should be contiguous in a list of modifiers but can potentially appear in two places (i.e., `[...leadingDecorators, ...leadingModifiers, ...trailingDecorators, ...trailingModifiers]`).
|
||||
// The leading modifiers *should* only contain `export` and `default` when trailingDecorators are present, but we'll handle errors for any other leading modifiers in the checker.
|
||||
// It is illegal to have both leadingDecorators and trailingDecorators, but we will report that as a grammar check in the checker.
|
||||
|
||||
// parse leading decorators
|
||||
if (allowDecorators && token() === SyntaxKind.AtToken) {
|
||||
let decorator;
|
||||
while (decorator = tryParseDecorator()) {
|
||||
list = append(list, decorator);
|
||||
}
|
||||
}
|
||||
|
||||
// parse leading modifiers
|
||||
while (modifier = tryParseModifier(hasSeenStaticModifier, permitConstAsModifier, stopOnStartOfClassStaticBlock)) {
|
||||
if (modifier.kind === SyntaxKind.StaticKeyword) hasSeenStaticModifier = true;
|
||||
list = append(list, modifier);
|
||||
hasLeadingModifier = true;
|
||||
}
|
||||
|
||||
// parse trailing decorators, but only if we parsed any leading modifiers
|
||||
if (hasLeadingModifier && allowDecorators && token() === SyntaxKind.AtToken) {
|
||||
while (decorator = tryParseDecorator()) {
|
||||
list = append(list, decorator);
|
||||
hasTrailingDecorator = true;
|
||||
}
|
||||
}
|
||||
|
||||
// parse trailing modifiers, but only if we parsed any trailing decorators
|
||||
if (hasTrailingDecorator) {
|
||||
while (modifier = tryParseModifier(hasSeenStaticModifier, permitConstAsModifier, stopOnStartOfClassStaticBlock)) {
|
||||
if (modifier.kind === SyntaxKind.StaticKeyword) hasSeenStaticModifier = true;
|
||||
list = append(list, modifier);
|
||||
|
||||
+16
-8
@@ -3,6 +3,7 @@ import {
|
||||
__String,
|
||||
addInternalEmitFlags,
|
||||
addRange,
|
||||
addRelatedInfo,
|
||||
append,
|
||||
arrayFrom,
|
||||
arrayIsEqualTo,
|
||||
@@ -2957,14 +2958,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
|
||||
}
|
||||
else if (isClassDeclaration(parent)) {
|
||||
const exportIndex = findIndex(parent.modifiers, isExportModifier);
|
||||
const defaultIndex = findIndex(parent.modifiers, isDefaultModifier);
|
||||
if (exportIndex >= 0 && decoratorIndex < exportIndex) {
|
||||
// report illegal decorator before `export default`
|
||||
diagnostics.push(createDiagnosticForNode(parent.modifiers[decoratorIndex], Diagnostics.Decorators_must_come_after_export_or_export_default_in_JavaScript_files));
|
||||
}
|
||||
else if (defaultIndex >= 0 && decoratorIndex < defaultIndex) {
|
||||
// report illegal decorator before `export default`
|
||||
diagnostics.push(createDiagnosticForNode(parent.modifiers[decoratorIndex], Diagnostics.Decorators_are_not_valid_here));
|
||||
if (exportIndex >= 0) {
|
||||
const defaultIndex = findIndex(parent.modifiers, isDefaultModifier);
|
||||
if (decoratorIndex > exportIndex && defaultIndex >= 0 && decoratorIndex < defaultIndex) {
|
||||
// report illegal decorator between `export` and `default`
|
||||
diagnostics.push(createDiagnosticForNode(parent.modifiers[decoratorIndex], Diagnostics.Decorators_are_not_valid_here));
|
||||
}
|
||||
else if (exportIndex >= 0 && decoratorIndex < exportIndex) {
|
||||
const trailingDecoratorIndex = findIndex(parent.modifiers, isDecorator, exportIndex);
|
||||
if (trailingDecoratorIndex >= 0) {
|
||||
diagnostics.push(addRelatedInfo(
|
||||
createDiagnosticForNode(parent.modifiers[trailingDecoratorIndex], Diagnostics.Decorators_may_not_appear_after_export_or_export_default_if_they_also_appear_before_export),
|
||||
createDiagnosticForNode(parent.modifiers[decoratorIndex], Diagnostics.Decorator_used_before_export_here),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+111
-94
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* The decorator context types provided to class member decorators.
|
||||
* The decorator context types provided to class element decorators.
|
||||
*/
|
||||
type ClassMemberDecoratorContext =
|
||||
| ClassMethodDecoratorContext
|
||||
@@ -60,34 +60,37 @@ interface ClassMethodDecoratorContext<
|
||||
This = unknown,
|
||||
Value extends (this: This, ...args: any) => any = (this: This, ...args: any) => any,
|
||||
> {
|
||||
/** The kind of class member that was decorated. */
|
||||
/** The kind of class element that was decorated. */
|
||||
readonly kind: "method";
|
||||
|
||||
/** The name of the decorated class member. */
|
||||
/** The name of the decorated class element. */
|
||||
readonly name: string | symbol;
|
||||
|
||||
/** A value indicating whether the class member is a static (`true`) or instance (`false`) member. */
|
||||
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
|
||||
readonly static: boolean;
|
||||
|
||||
/** A value indicating whether the class member has a private name. */
|
||||
/** A value indicating whether the class element has a private name. */
|
||||
readonly private: boolean;
|
||||
|
||||
// NOTE: Disabled, pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
|
||||
// /** An object that can be used to access the current value of the class member at runtime. */
|
||||
// readonly access: {
|
||||
// /**
|
||||
// * Gets the current value of the method from the provided receiver.
|
||||
// *
|
||||
// * @example
|
||||
// * let fn = context.access.get.call(instance);
|
||||
// */
|
||||
// get(this: This): Value;
|
||||
// };
|
||||
/** An object that can be used to access the current value of the class element at runtime. */
|
||||
readonly access: {
|
||||
/**
|
||||
* Determines whether an object has a property with the same name as the decorated element.
|
||||
*/
|
||||
has(object: This): boolean;
|
||||
/**
|
||||
* Gets the current value of the method from the provided object.
|
||||
*
|
||||
* @example
|
||||
* let fn = context.access.get(instance);
|
||||
*/
|
||||
get(object: This): Value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a callback to be invoked either before static initializers are run (when
|
||||
* decorating a `static` member), or before instance initializers are run (when
|
||||
* decorating a non-`static` member).
|
||||
* decorating a `static` element), or before instance initializers are run (when
|
||||
* decorating a non-`static` element).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
@@ -121,34 +124,37 @@ interface ClassGetterDecoratorContext<
|
||||
This = unknown,
|
||||
Value = unknown,
|
||||
> {
|
||||
/** The kind of class member that was decorated. */
|
||||
/** The kind of class element that was decorated. */
|
||||
readonly kind: "getter";
|
||||
|
||||
/** The name of the decorated class member. */
|
||||
/** The name of the decorated class element. */
|
||||
readonly name: string | symbol;
|
||||
|
||||
/** A value indicating whether the class member is a static (`true`) or instance (`false`) member. */
|
||||
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
|
||||
readonly static: boolean;
|
||||
|
||||
/** A value indicating whether the class member has a private name. */
|
||||
/** A value indicating whether the class element has a private name. */
|
||||
readonly private: boolean;
|
||||
|
||||
// NOTE: Disabled, pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
|
||||
// /** An object that can be used to access the current value of the class member at runtime. */
|
||||
// readonly access: {
|
||||
// /**
|
||||
// * Invokes the getter on the provided receiver.
|
||||
// *
|
||||
// * @example
|
||||
// * let value = context.access.get.call(instance);
|
||||
// */
|
||||
// get(this: This): Value;
|
||||
// };
|
||||
/** An object that can be used to access the current value of the class element at runtime. */
|
||||
readonly access: {
|
||||
/**
|
||||
* Determines whether an object has a property with the same name as the decorated element.
|
||||
*/
|
||||
has(object: This): boolean;
|
||||
/**
|
||||
* Invokes the getter on the provided object.
|
||||
*
|
||||
* @example
|
||||
* let value = context.access.get(instance);
|
||||
*/
|
||||
get(object: This): Value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a callback to be invoked either before static initializers are run (when
|
||||
* decorating a `static` member), or before instance initializers are run (when
|
||||
* decorating a non-`static` member).
|
||||
* decorating a `static` element), or before instance initializers are run (when
|
||||
* decorating a non-`static` element).
|
||||
*/
|
||||
addInitializer(initializer: (this: This) => void): void;
|
||||
}
|
||||
@@ -163,34 +169,37 @@ interface ClassSetterDecoratorContext<
|
||||
This = unknown,
|
||||
Value = unknown,
|
||||
> {
|
||||
/** The kind of class member that was decorated. */
|
||||
/** The kind of class element that was decorated. */
|
||||
readonly kind: "setter";
|
||||
|
||||
/** The name of the decorated class member. */
|
||||
/** The name of the decorated class element. */
|
||||
readonly name: string | symbol;
|
||||
|
||||
/** A value indicating whether the class member is a static (`true`) or instance (`false`) member. */
|
||||
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
|
||||
readonly static: boolean;
|
||||
|
||||
/** A value indicating whether the class member has a private name. */
|
||||
/** A value indicating whether the class element has a private name. */
|
||||
readonly private: boolean;
|
||||
|
||||
// NOTE: Disabled, pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
|
||||
/** An object that can be used to access the current value of the class member at runtime. */
|
||||
// readonly access: {
|
||||
// /**
|
||||
// * Invokes the setter on the provided receiver.
|
||||
// *
|
||||
// * @example
|
||||
// * context.access.set.call(instance, value);
|
||||
// */
|
||||
// set(this: This, value: Value): void;
|
||||
// };
|
||||
/** An object that can be used to access the current value of the class element at runtime. */
|
||||
readonly access: {
|
||||
/**
|
||||
* Determines whether an object has a property with the same name as the decorated element.
|
||||
*/
|
||||
has(object: This): boolean;
|
||||
/**
|
||||
* Invokes the setter on the provided object.
|
||||
*
|
||||
* @example
|
||||
* context.access.set(instance, value);
|
||||
*/
|
||||
set(object: This, value: Value): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a callback to be invoked either before static initializers are run (when
|
||||
* decorating a `static` member), or before instance initializers are run (when
|
||||
* decorating a non-`static` member).
|
||||
* decorating a `static` element), or before instance initializers are run (when
|
||||
* decorating a non-`static` element).
|
||||
*/
|
||||
addInitializer(initializer: (this: This) => void): void;
|
||||
}
|
||||
@@ -205,42 +214,46 @@ interface ClassAccessorDecoratorContext<
|
||||
This = unknown,
|
||||
Value = unknown,
|
||||
> {
|
||||
/** The kind of class member that was decorated. */
|
||||
/** The kind of class element that was decorated. */
|
||||
readonly kind: "accessor";
|
||||
|
||||
/** The name of the decorated class member. */
|
||||
/** The name of the decorated class element. */
|
||||
readonly name: string | symbol;
|
||||
|
||||
/** A value indicating whether the class member is a static (`true`) or instance (`false`) member. */
|
||||
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
|
||||
readonly static: boolean;
|
||||
|
||||
/** A value indicating whether the class member has a private name. */
|
||||
/** A value indicating whether the class element has a private name. */
|
||||
readonly private: boolean;
|
||||
|
||||
// NOTE: Disabled, pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
|
||||
// /** An object that can be used to access the current value of the class member at runtime. */
|
||||
// readonly access: {
|
||||
// /**
|
||||
// * Invokes the getter on the provided receiver.
|
||||
// *
|
||||
// * @example
|
||||
// * let value = context.access.get.call(instance);
|
||||
// */
|
||||
// get(this: This): Value;
|
||||
/** An object that can be used to access the current value of the class element at runtime. */
|
||||
readonly access: {
|
||||
/**
|
||||
* Determines whether an object has a property with the same name as the decorated element.
|
||||
*/
|
||||
has(object: This): boolean;
|
||||
|
||||
// /**
|
||||
// * Invokes the setter on the provided receiver.
|
||||
// *
|
||||
// * @example
|
||||
// * context.access.set.call(instance, value);
|
||||
// */
|
||||
// set(this: This, value: Value): void;
|
||||
// };
|
||||
/**
|
||||
* Invokes the getter on the provided object.
|
||||
*
|
||||
* @example
|
||||
* let value = context.access.get(instance);
|
||||
*/
|
||||
get(object: This): Value;
|
||||
|
||||
/**
|
||||
* Invokes the setter on the provided object.
|
||||
*
|
||||
* @example
|
||||
* context.access.set(instance, value);
|
||||
*/
|
||||
set(object: This, value: Value): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a callback to be invoked either before static initializers are run (when
|
||||
* decorating a `static` member), or before instance initializers are run (when
|
||||
* decorating a non-`static` member).
|
||||
* decorating a `static` element), or before instance initializers are run (when
|
||||
* decorating a non-`static` element).
|
||||
*/
|
||||
addInitializer(initializer: (this: This) => void): void;
|
||||
}
|
||||
@@ -302,36 +315,40 @@ interface ClassFieldDecoratorContext<
|
||||
This = unknown,
|
||||
Value = unknown,
|
||||
> {
|
||||
/** The kind of class member that was decorated. */
|
||||
/** The kind of class element that was decorated. */
|
||||
readonly kind: "field";
|
||||
|
||||
/** The name of the decorated class member. */
|
||||
/** The name of the decorated class element. */
|
||||
readonly name: string | symbol;
|
||||
|
||||
/** A value indicating whether the class member is a static (`true`) or instance (`false`) member. */
|
||||
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
|
||||
readonly static: boolean;
|
||||
|
||||
/** A value indicating whether the class member has a private name. */
|
||||
/** A value indicating whether the class element has a private name. */
|
||||
readonly private: boolean;
|
||||
|
||||
// NOTE: Disabled, pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
|
||||
// /** An object that can be used to access the current value of the class member at runtime. */
|
||||
// readonly access: {
|
||||
// /**
|
||||
// * Gets the value of the field on the provided receiver.
|
||||
// */
|
||||
// get(this: This): Value;
|
||||
/** An object that can be used to access the current value of the class element at runtime. */
|
||||
readonly access: {
|
||||
/**
|
||||
* Determines whether an object has a property with the same name as the decorated element.
|
||||
*/
|
||||
has(object: This): boolean;
|
||||
|
||||
// /**
|
||||
// * Sets the value of the field on the provided receiver.
|
||||
// */
|
||||
// set(this: This, value: Value): void;
|
||||
// };
|
||||
/**
|
||||
* Gets the value of the field on the provided object.
|
||||
*/
|
||||
get(object: This): Value;
|
||||
|
||||
/**
|
||||
* Sets the value of the field on the provided object.
|
||||
*/
|
||||
set(object: This, value: Value): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a callback to be invoked either before static initializers are run (when
|
||||
* decorating a `static` member), or before instance initializers are run (when
|
||||
* decorating a non-`static` member).
|
||||
* decorating a `static` element), or before instance initializers are run (when
|
||||
* decorating a non-`static` element).
|
||||
*/
|
||||
addInitializer(initializer: (this: This) => void): void;
|
||||
}
|
||||
|
||||
@@ -704,7 +704,7 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
});
|
||||
});
|
||||
// Disabled pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
|
||||
describe.skip(".access", () => {
|
||||
describe(".access", () => {
|
||||
describe("for: class", () => {
|
||||
it("is not set", () => {
|
||||
const { context } = exec`
|
||||
@@ -716,7 +716,7 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
});
|
||||
});
|
||||
describe("for: method", () => {
|
||||
it("is { get }", () => {
|
||||
it("is { has, get }", () => {
|
||||
const { context } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -725,10 +725,11 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
}
|
||||
`;
|
||||
assert.isObject(context.access);
|
||||
assert.hasAllKeys(context.access, ["get"]);
|
||||
assert.hasAllKeys(context.access, ["has", "get"]);
|
||||
assert.isFunction(context.access.has);
|
||||
assert.isFunction(context.access.get);
|
||||
});
|
||||
it("accesses value using 'this'", () => {
|
||||
it("test public element presence via .has", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -736,27 +737,56 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
static method() {}
|
||||
}
|
||||
`;
|
||||
assert.strictEqual(context.access.get.call(C), C.method);
|
||||
|
||||
const obj = { method() {} };
|
||||
assert.strictEqual(context.access.get.call(obj), obj.method);
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isTrue(context.access.has({ method() {} }));
|
||||
assert.isFalse(context.access.has({ }));
|
||||
});
|
||||
it("can access value for private name", () => {
|
||||
const { context, C } = exec`
|
||||
it("test private element presence via .has", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static #method() {}
|
||||
}
|
||||
export class D {
|
||||
static #method() {}
|
||||
}
|
||||
`;
|
||||
assert.isFunction(context.access.get.call(C));
|
||||
|
||||
const obj = { ["#method"]() {} };
|
||||
assert.throws(() => context.access.get.call(obj));
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isFalse(context.access.has(D));
|
||||
assert.isFalse(context.access.has({ }));
|
||||
});
|
||||
it("read public element of argument", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static method() {}
|
||||
}
|
||||
`;
|
||||
assert.strictEqual(context.access.get(C), C.method);
|
||||
const obj = { method() {} };
|
||||
assert.isTrue(context.access.has(obj));
|
||||
assert.strictEqual(context.access.get(obj), obj.method);
|
||||
});
|
||||
it("read private element of argument", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static #method() {}
|
||||
}
|
||||
export class D {
|
||||
static #method() {}
|
||||
}
|
||||
`;
|
||||
assert.isFunction(context.access.get(C));
|
||||
assert.throws(() => context.access.get(D));
|
||||
assert.throws(() => context.access.get({ ["#method"]() {} }));
|
||||
});
|
||||
});
|
||||
describe("for: getter", () => {
|
||||
it("is { get }", () => {
|
||||
it("is { has, get }", () => {
|
||||
const { context } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -765,10 +795,11 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
}
|
||||
`;
|
||||
assert.isObject(context.access);
|
||||
assert.hasAllKeys(context.access, ["get"]);
|
||||
assert.hasAllKeys(context.access, ["has", "get"]);
|
||||
assert.isFunction(context.access.has);
|
||||
assert.isFunction(context.access.get);
|
||||
});
|
||||
it("accesses value using 'this'", () => {
|
||||
it("test public element presence via .has", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -776,27 +807,56 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
static get x() { return 1; }
|
||||
}
|
||||
`;
|
||||
assert.strictEqual(context.access.get.call(C), 1);
|
||||
|
||||
const obj = { x: 2 };
|
||||
assert.strictEqual(context.access.get.call(obj), 2);
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isTrue(context.access.has({
|
||||
get x() { return 2; }
|
||||
}));
|
||||
assert.isFalse(context.access.has({ }));
|
||||
});
|
||||
it("can access value for private name", () => {
|
||||
it("test private element presence via .has", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static #method() {}
|
||||
}
|
||||
export class D {
|
||||
static #method() {}
|
||||
}
|
||||
`;
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isFalse(context.access.has(D));
|
||||
assert.isFalse(context.access.has({ }));
|
||||
});
|
||||
it("read public element of argument", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static get x() { return 1; }
|
||||
}
|
||||
`;
|
||||
assert.strictEqual(context.access.get(C), 1);
|
||||
assert.strictEqual(context.access.get({ x: 2 }), 2);
|
||||
});
|
||||
it("read private element of argument", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static get #x() { return 1; }
|
||||
}
|
||||
export class D {
|
||||
static get #x() { return 1; }
|
||||
}
|
||||
`;
|
||||
assert.strictEqual(context.access.get.call(C), 1);
|
||||
|
||||
const obj = { "#x": 2 };
|
||||
assert.throws(() => context.access.get.call(obj));
|
||||
assert.strictEqual(context.access.get(C), 1);
|
||||
assert.throws(() => context.access.get(D));
|
||||
assert.throws(() => context.access.get({ "#x": 2 }));
|
||||
});
|
||||
});
|
||||
describe("for: setter", () => {
|
||||
it("is { set }", () => {
|
||||
it("is { has, set }", () => {
|
||||
const { context } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -805,10 +865,38 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
}
|
||||
`;
|
||||
assert.isObject(context.access);
|
||||
assert.hasAllKeys(context.access, ["set"]);
|
||||
assert.hasAllKeys(context.access, ["has", "set"]);
|
||||
assert.isFunction(context.access.has);
|
||||
assert.isFunction(context.access.set);
|
||||
});
|
||||
it("accesses value using 'this'", () => {
|
||||
it("test public element presence via .has", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static set x(v: number) { }
|
||||
}
|
||||
`;
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isTrue(context.access.has({ x: 2 }));
|
||||
assert.isFalse(context.access.has({ }));
|
||||
});
|
||||
it("test private element presence via .has", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static set #x(v: number) { }
|
||||
}
|
||||
export class D {
|
||||
static set #x(v: number) { }
|
||||
}
|
||||
`;
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isFalse(context.access.has(D));
|
||||
assert.isFalse(context.access.has({ "#x": 2 }));
|
||||
});
|
||||
it("write public element of argument", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -817,29 +905,31 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
static y: number;
|
||||
}
|
||||
`;
|
||||
context.access.set.call(C, 1);
|
||||
context.access.set(C, 1);
|
||||
assert.strictEqual(C.y, 1);
|
||||
|
||||
const obj = { x: 2 };
|
||||
context.access.set.call(obj, 3);
|
||||
context.access.set(obj, 3);
|
||||
assert.strictEqual(obj.x, 3);
|
||||
});
|
||||
it("can access value for private name", () => {
|
||||
const { context, C } = exec`
|
||||
it("write private element of argument", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static set #x(v: number) {}
|
||||
}
|
||||
export class D {
|
||||
static set #x(v: number) {}
|
||||
}
|
||||
`;
|
||||
context.access.set.call(C, 1);
|
||||
|
||||
const obj = { "#x": 2 };
|
||||
assert.throws(() => context.access.set.call(obj, 3));
|
||||
context.access.set(C, 1);
|
||||
assert.throws(() => context.access.set(D, 3));
|
||||
assert.throws(() => context.access.set({ "#x": 2 }, 3));
|
||||
});
|
||||
});
|
||||
describe("for: field", () => {
|
||||
it("is { get, set }", () => {
|
||||
it("is { has, get, set }", () => {
|
||||
const { context } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -848,11 +938,39 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
}
|
||||
`;
|
||||
assert.isObject(context.access);
|
||||
assert.hasAllKeys(context.access, ["get", "set"]);
|
||||
assert.hasAllKeys(context.access, ["has", "get", "set"]);
|
||||
assert.isFunction(context.access.has);
|
||||
assert.isFunction(context.access.get);
|
||||
assert.isFunction(context.access.set);
|
||||
});
|
||||
it("accesses value using 'this'", () => {
|
||||
it("test public element presence via .has", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static x: number = 1;
|
||||
}
|
||||
`;
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isTrue(context.access.has({ x: 2 }));
|
||||
assert.isFalse(context.access.has({ }));
|
||||
});
|
||||
it("test private element presence via .has", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static #x: number = 1;
|
||||
}
|
||||
export class D {
|
||||
static #x: number = 1;
|
||||
}
|
||||
`;
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isFalse(context.access.has(D));
|
||||
assert.isFalse(context.access.has({ "#x": 2 }));
|
||||
});
|
||||
it("read/write public element of argument", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -861,36 +979,41 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
}
|
||||
`;
|
||||
|
||||
assert.strictEqual(context.access.get.call(C), 1);
|
||||
context.access.set.call(C, 2);
|
||||
assert.strictEqual(context.access.get(C), 1);
|
||||
context.access.set(C, 2);
|
||||
assert.strictEqual(C.x, 2);
|
||||
|
||||
const obj = { x: 2 };
|
||||
assert.strictEqual(context.access.get.call(obj), 2);
|
||||
context.access.set.call(obj, 3);
|
||||
assert.strictEqual(context.access.get(obj), 2);
|
||||
context.access.set(obj, 3);
|
||||
assert.strictEqual(obj.x, 3);
|
||||
});
|
||||
it("can access value for private name", () => {
|
||||
const { context, C } = exec`
|
||||
it("read/write private element of argument", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static #x: number = 1;
|
||||
static getX() { return this.#x; }
|
||||
}
|
||||
export class D {
|
||||
static #x: number = 1;
|
||||
}
|
||||
`;
|
||||
|
||||
assert.strictEqual(context.access.get.call(C), 1);
|
||||
context.access.set.call(C, 2);
|
||||
assert.strictEqual(context.access.get(C), 1);
|
||||
context.access.set(C, 2);
|
||||
assert.strictEqual(C.getX(), 2);
|
||||
|
||||
const obj = { "#x": 2 };
|
||||
assert.throws(() => context.access.get.call(obj));
|
||||
assert.throws(() => context.access.set.call(obj, 3));
|
||||
assert.throws(() => context.access.get(D));
|
||||
assert.throws(() => context.access.set(D, 3));
|
||||
|
||||
assert.throws(() => context.access.get({ "#x": 2 }));
|
||||
assert.throws(() => context.access.set({ "#x": 2 }, 3));
|
||||
});
|
||||
});
|
||||
describe("for: auto-accessor", () => {
|
||||
it("is { get, set }", () => {
|
||||
it("is { has, get, set }", () => {
|
||||
const { context } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -898,11 +1021,39 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
static accessor x: number;
|
||||
}
|
||||
`;
|
||||
assert.hasAllKeys(context.access, ["get", "set"]);
|
||||
assert.hasAllKeys(context.access, ["has", "get", "set"]);
|
||||
assert.isFunction(context.access.has);
|
||||
assert.isFunction(context.access.get);
|
||||
assert.isFunction(context.access.set);
|
||||
});
|
||||
it("accesses value using 'this'", () => {
|
||||
it("test public element presence via .has", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static accessor x: number = 1;
|
||||
}
|
||||
`;
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isTrue(context.access.has({ x: 2 }));
|
||||
assert.isFalse(context.access.has({ }));
|
||||
});
|
||||
it("test private element presence via .has", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static accessor #x: number = 1;
|
||||
}
|
||||
export class D {
|
||||
static accessor #x: number = 1;
|
||||
}
|
||||
`;
|
||||
assert.isTrue(context.access.has(C));
|
||||
assert.isFalse(context.access.has(D));
|
||||
assert.isFalse(context.access.has({ "#x": 2 }));
|
||||
});
|
||||
it("read/write public element of argument", () => {
|
||||
const { context, C } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@@ -911,32 +1062,37 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
}
|
||||
`;
|
||||
|
||||
assert.strictEqual(context.access.get.call(C), 1);
|
||||
context.access.set.call(C, 2);
|
||||
assert.strictEqual(context.access.get(C), 1);
|
||||
context.access.set(C, 2);
|
||||
assert.strictEqual(C.x, 2);
|
||||
|
||||
const obj = { x: 2 };
|
||||
assert.strictEqual(context.access.get.call(obj), 2);
|
||||
context.access.set.call(obj, 3);
|
||||
assert.strictEqual(context.access.get(obj), 2);
|
||||
context.access.set(obj, 3);
|
||||
assert.strictEqual(obj.x, 3);
|
||||
});
|
||||
it("can access value for private name", () => {
|
||||
const { context, C } = exec`
|
||||
it("read/write private element of argument", () => {
|
||||
const { context, C, D } = exec`
|
||||
export let context;
|
||||
export class C {
|
||||
@((t, c) => { context = c; })
|
||||
static accessor #x: number = 1;
|
||||
static getX() { return this.#x; }
|
||||
}
|
||||
export class D {
|
||||
static accessor #x: number = 1;
|
||||
}
|
||||
`;
|
||||
|
||||
assert.strictEqual(context.access.get.call(C), 1);
|
||||
context.access.set.call(C, 2);
|
||||
assert.strictEqual(context.access.get(C), 1);
|
||||
context.access.set(C, 2);
|
||||
assert.strictEqual(C.getX(), 2);
|
||||
|
||||
const obj = { "#x": 2 };
|
||||
assert.throws(() => context.access.get.call(obj));
|
||||
assert.throws(() => context.access.set.call(obj, 3));
|
||||
assert.throws(() => context.access.get(D));
|
||||
assert.throws(() => context.access.set(D, 3));
|
||||
|
||||
assert.throws(() => context.access.get({ "#x": 2 }));
|
||||
assert.throws(() => context.access.set({ "#x": 2 }, 3));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2223,13 +2379,12 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
});
|
||||
|
||||
// see https://github.com/tc39/proposal-decorators#access-and-metadata-sidechanneling
|
||||
// Disabled, pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
|
||||
it.skip(`dependency injection (${targetName})`, () => {
|
||||
it(`dependency injection (${targetName})`, () => {
|
||||
const { result } = exec`
|
||||
const INJECTIONS = new WeakMap<object, { injectionKey: string, set: (this: any, value: any) => void }[]>();
|
||||
const INJECTIONS = new WeakMap<object, { injectionKey: string, set: (object: any, value: any) => void }[]>();
|
||||
|
||||
function createInjections() {
|
||||
const injections: { injectionKey: string, set: (this: any, value: any) => void }[] = [];
|
||||
const injections: { injectionKey: string, set: (object: any, value: any) => void }[] = [];
|
||||
|
||||
function injectable<T extends new (...args: any) => any>(Class: T, context: ClassDecoratorContext<T>) {
|
||||
INJECTIONS.set(Class, injections);
|
||||
@@ -2259,7 +2414,7 @@ describe("unittests:: evaluation:: esDecorators", () => {
|
||||
let instance = new Class();
|
||||
|
||||
for (const { injectionKey, set } of INJECTIONS.get(Class) || []) {
|
||||
set.call(instance, this.lookup(injectionKey));
|
||||
set(instance, this.lookup(injectionKey));
|
||||
}
|
||||
|
||||
return instance;
|
||||
|
||||
Reference in New Issue
Block a user