ES private field check (#44648)

* es private fields in in (#52)

add support for the 'private-fields-in-in' TC39 proposal

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [fixup] include inToken when walking forEachChild(node, cb)

* [squash] re-accept lib definition baseline changes

* [squash] reduce if/else to ternary

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] drop 'originalName' and rename parameter instead

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] extend spelling suggestion to all privateIdentifiers

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] revert the added lexical spelling suggestions logic

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] update baseline

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] inline variable as per PR suggestion

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] test targets both esnext and es2020 as per PR comment

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* switch to using a binary expression

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] PrivateIdentifier now extends PrimaryExpression

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] accept public api baseline changes

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] classPrivateFieldInHelper now has documentation

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] type-check now follows existing in-expression path

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] parser now follows existing binaryExpression path

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] correct typo in comment

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] no longer use esNext flag

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] swap 'reciever, state' helper params

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] remove change to parenthesizerRules

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] apply suggested changes to checker

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] remove need for assertion in fixSpelling

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] improve comment hint in test

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] fix comment typos

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] add flow-test for Foo | FooSub | Bar

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] add checkExternalEmitHelpers call and new test case

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] simplify and correct parser

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] move most of the added checker logic to expression level

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] always error when privateId could not be resolved

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] reword comment

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] fix codeFixSpelling test

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] do less work

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* store symbol by priateId not binaryExpression

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* moved parsePrivateIdentifier into parsePrimaryExpression

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] checkInExpressionn bails out early on silentNeverType

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] more detailed error messages

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] resolves conflict in diagnosticMessages.json

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] update baseline for importHelpersES6

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] remove redundent if and comment from parser

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] split up grammar/check/symbolLookup

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>

* [squash] reword message for existing left side of in-expression error

Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>
This commit is contained in:
Ashley Claymore 2021-09-24 17:05:27 +01:00 committed by GitHub
parent 59fb3731e3
commit af689cc5d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 2398 additions and 45 deletions

View file

@ -23917,6 +23917,9 @@ namespace ts {
case SyntaxKind.InstanceOfKeyword:
return narrowTypeByInstanceof(type, expr, assumeTrue);
case SyntaxKind.InKeyword:
if (isPrivateIdentifier(expr.left)) {
return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue);
}
const target = getReferenceCandidate(expr.right);
const leftType = getTypeOfNode(expr.left);
if (leftType.flags & TypeFlags.StringLiteral) {
@ -23947,6 +23950,24 @@ namespace ts {
return type;
}
function narrowTypeByPrivateIdentifierInInExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
const target = getReferenceCandidate(expr.right);
if (!isMatchingReference(reference, target)) {
return type;
}
Debug.assertNode(expr.left, isPrivateIdentifier);
const symbol = getSymbolForPrivateIdentifierExpression(expr.left);
if (symbol === undefined) {
return type;
}
const classSymbol = symbol.parent!;
const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration"))
? getTypeOfSymbol(classSymbol) as InterfaceType
: getDeclaredTypeOfSymbol(classSymbol);
return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
}
function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
// We are in a branch of obj?.foo === value (or any one of the other equality operators). We narrow obj as follows:
// When operator is === and type of value excludes undefined, null and undefined is removed from type of obj in true branch.
@ -27790,6 +27811,40 @@ namespace ts {
}
}
function checkGrammarPrivateIdentifierExpression(privId: PrivateIdentifier): boolean {
if (!getContainingClass(privId)) {
return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies);
}
if (!isExpressionNode(privId)) {
return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression);
}
if (!getSymbolForPrivateIdentifierExpression(privId)) {
return grammarErrorOnNode(privId, Diagnostics.Cannot_find_name_0, idText(privId));
}
return false;
}
function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type {
checkGrammarPrivateIdentifierExpression(privId);
const symbol = getSymbolForPrivateIdentifierExpression(privId);
if (symbol) {
markPropertyAsReferenced(symbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false);
}
return anyType;
}
function getSymbolForPrivateIdentifierExpression(privId: PrivateIdentifier): Symbol | undefined {
if (!isExpressionNode(privId)) {
return undefined;
}
const links = getNodeLinks(privId);
if (links.resolvedSymbol === undefined) {
links.resolvedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId);
}
return links.resolvedSymbol;
}
function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined {
return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName);
}
@ -32110,11 +32165,29 @@ namespace ts {
if (leftType === silentNeverType || rightType === silentNeverType) {
return silentNeverType;
}
leftType = checkNonNullType(leftType, left);
if (isPrivateIdentifier(left)) {
if (languageVersion < ScriptTarget.ESNext) {
checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn);
}
// Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type
// which provides us with the opportunity to emit more detailed errors
if (!getNodeLinks(left).resolvedSymbol && getContainingClass(left)) {
const isUncheckedJS = isUncheckedJSSuggestion(left, rightType.symbol, /*excludeClasses*/ true);
reportNonexistentProperty(left, rightType, isUncheckedJS);
}
}
else {
leftType = checkNonNullType(leftType, left);
// TypeScript 1.0 spec (April 2014): 4.15.5
// Require the left operand to be of type Any, the String primitive type, or the Number primitive type.
if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) ||
isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) {
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_a_private_identifier_or_of_type_any_string_number_or_symbol);
}
}
rightType = checkNonNullType(rightType, right);
// TypeScript 1.0 spec (April 2014): 4.15.5
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
// and the right operand to be
// The in operator requires the right operand to be
//
// 1. assignable to the non-primitive type,
// 2. an unconstrained type parameter,
@ -32132,10 +32205,6 @@ namespace ts {
// unless *all* instantiations would result in an error.
//
// The result is always of the Boolean primitive type.
if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) ||
isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) {
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
}
const rightTypeConstraint = getConstraintOfType(rightType);
if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) ||
rightTypeConstraint && (
@ -33517,6 +33586,8 @@ namespace ts {
switch (kind) {
case SyntaxKind.Identifier:
return checkIdentifier(node as Identifier, checkMode);
case SyntaxKind.PrivateIdentifier:
return checkPrivateIdentifierExpression(node as PrivateIdentifier);
case SyntaxKind.ThisKeyword:
return checkThisExpression(node);
case SyntaxKind.SuperKeyword:
@ -40296,6 +40367,9 @@ namespace ts {
}
return result;
}
else if (isPrivateIdentifier(name)) {
return getSymbolForPrivateIdentifierExpression(name);
}
else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) {
const links = getNodeLinks(name);
if (links.resolvedSymbol) {
@ -41712,6 +41786,7 @@ namespace ts {
case ExternalEmitHelpers.MakeTemplateObject: return "__makeTemplateObject";
case ExternalEmitHelpers.ClassPrivateFieldGet: return "__classPrivateFieldGet";
case ExternalEmitHelpers.ClassPrivateFieldSet: return "__classPrivateFieldSet";
case ExternalEmitHelpers.ClassPrivateFieldIn: return "__classPrivateFieldIn";
case ExternalEmitHelpers.CreateBinding: return "__createBinding";
default: return Debug.fail("Unrecognized helper");
}

View file

@ -1392,6 +1392,10 @@
"category": "Message",
"code": 1450
},
"Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression": {
"category": "Error",
"code": 1451
},
"The types of '{0}' are incompatible between these types.": {
"category": "Error",
@ -1654,7 +1658,7 @@
"category": "Error",
"code": 2359
},
"The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
"The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.": {
"category": "Error",
"code": 2360
},

View file

@ -1684,6 +1684,8 @@ namespace ts {
// Identifiers
case SyntaxKind.Identifier:
return emitIdentifier(node as Identifier);
case SyntaxKind.PrivateIdentifier:
return emitPrivateIdentifier(node as PrivateIdentifier);
// Expressions
case SyntaxKind.ArrayLiteralExpression:

View file

@ -34,6 +34,7 @@ namespace ts {
// Class Fields Helpers
createClassPrivateFieldGetHelper(receiver: Expression, state: Identifier, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression;
createClassPrivateFieldSetHelper(receiver: Expression, state: Identifier, value: Expression, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression;
createClassPrivateFieldInHelper(state: Identifier, receiver: Expression): Expression;
}
export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory {
@ -75,6 +76,7 @@ namespace ts {
// Class Fields Helpers
createClassPrivateFieldGetHelper,
createClassPrivateFieldSetHelper,
createClassPrivateFieldInHelper
};
/**
@ -395,6 +397,10 @@ namespace ts {
return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, args);
}
function createClassPrivateFieldInHelper(state: Identifier, receiver: Expression) {
context.requestEmitHelper(classPrivateFieldInHelper);
return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [state, receiver]);
}
}
/* @internal */
@ -961,6 +967,29 @@ namespace ts {
};`
};
/**
* Parameters:
* @param state One of the following:
* - A WeakMap when the member is a private instance field.
* - A WeakSet when the member is a private instance method or accessor.
* - A function value that should be the undecorated class constructor when the member is a private static field, method, or accessor.
* @param receiver The object being checked if it has the private member.
*
* Usage:
* This helper is used to transform `#field in expression` to
* `__classPrivateFieldIn(<weakMap/weakSet/constructor>, expression)`
*/
export const classPrivateFieldInHelper: UnscopedEmitHelper = {
name: "typescript:classPrivateFieldIn",
importName: "__classPrivateFieldIn",
scoped: false,
text: `
var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(state, receiver) {
if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object");
return typeof state === "function" ? receiver === state : state.has(receiver);
};`
};
let allUnscopedEmitHelpers: ReadonlyESMap<string, UnscopedEmitHelper> | undefined;
export function getAllUnscopedEmitHelpers() {
@ -986,6 +1015,7 @@ namespace ts {
exportStarHelper,
classPrivateFieldGetHelper,
classPrivateFieldSetHelper,
classPrivateFieldInHelper,
createBindingHelper,
setModuleDefaultHelper
], helper => helper.name));

View file

@ -452,4 +452,4 @@ namespace ts {
parenthesizeConstituentTypesOfUnionOrIntersectionType: nodes => cast(nodes, isNodeArray),
parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray),
};
}
}

View file

@ -5617,6 +5617,8 @@ namespace ts {
break;
case SyntaxKind.TemplateHead:
return parseTemplateExpression(/* isTaggedTemplate */ false);
case SyntaxKind.PrivateIdentifier:
return parsePrivateIdentifier();
}
return parseIdentifier(Diagnostics.Expression_expected);

View file

@ -266,15 +266,44 @@ namespace ts {
/**
* If we visit a private name, this means it is an undeclared private name.
* Replace it with an empty identifier to indicate a problem with the code.
* Replace it with an empty identifier to indicate a problem with the code,
* unless we are in a statement position - otherwise this will not trigger
* a SyntaxError.
*/
function visitPrivateIdentifier(node: PrivateIdentifier) {
if (!shouldTransformPrivateElementsOrClassStaticBlocks) {
return node;
}
if (isStatement(node.parent)) {
return node;
}
return setOriginalNode(factory.createIdentifier(""), node);
}
/**
* Visits `#id in expr`
*/
function visitPrivateIdentifierInInExpression(node: BinaryExpression) {
if (!shouldTransformPrivateElementsOrClassStaticBlocks) {
return node;
}
const privId = node.left;
Debug.assertNode(privId, isPrivateIdentifier);
Debug.assert(node.operatorToken.kind === SyntaxKind.InKeyword);
const info = accessPrivateIdentifier(privId);
if (info) {
const receiver = visitNode(node.right, visitor, isExpression);
return setOriginalNode(
context.getEmitHelperFactory().createClassPrivateFieldInHelper(info.brandCheckIdentifier, receiver),
node
);
}
// Private name has not been declared. Subsequent transformers will handle this error
return visitEachChild(node, visitor, context);
}
/**
* Visits the members of a class that has fields.
*
@ -827,6 +856,9 @@ namespace ts {
}
}
}
if (node.operatorToken.kind === SyntaxKind.InKeyword && isPrivateIdentifier(node.left)) {
return visitPrivateIdentifierInInExpression(node);
}
return visitEachChild(node, visitor, context);
}

View file

@ -1214,7 +1214,8 @@ namespace ts {
readonly expression: Expression;
}
export interface PrivateIdentifier extends Node {
// Typed as a PrimaryExpression due to its presence in BinaryExpressions (#field in expr)
export interface PrivateIdentifier extends PrimaryExpression {
readonly kind: SyntaxKind.PrivateIdentifier;
// escaping not strictly necessary
// avoids gotchas in transforms and utils
@ -6854,7 +6855,8 @@ namespace ts {
MakeTemplateObject = 1 << 18, // __makeTemplateObject (used for constructing template string array objects)
ClassPrivateFieldGet = 1 << 19, // __classPrivateFieldGet (used by the class private field transformation)
ClassPrivateFieldSet = 1 << 20, // __classPrivateFieldSet (used by the class private field transformation)
CreateBinding = 1 << 21, // __createBinding (use by the module transform for (re)exports and namespace imports)
ClassPrivateFieldIn = 1 << 21, // __classPrivateFieldIn (used by the class private field transformation)
CreateBinding = 1 << 22, // __createBinding (use by the module transform for (re)exports and namespace imports)
FirstEmitHelper = Extends,
LastEmitHelper = CreateBinding,

View file

@ -1949,6 +1949,8 @@ namespace ts {
node = node.parent;
}
return node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node);
case SyntaxKind.PrivateIdentifier:
return isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.InKeyword;
case SyntaxKind.Identifier:
if (node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node)) {
return true;
@ -3706,6 +3708,7 @@ namespace ts {
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.Identifier:
case SyntaxKind.PrivateIdentifier:
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:

View file

@ -1516,6 +1516,7 @@ namespace ts {
case SyntaxKind.ClassExpression:
case SyntaxKind.FunctionExpression:
case SyntaxKind.Identifier:
case SyntaxKind.PrivateIdentifier: // technically this is only an Expression if it's in a `#field in expr` BinaryExpression
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.BigIntLiteral:

View file

@ -4,7 +4,7 @@ namespace ts.codefix {
const didYouMeanStaticMemberCode = Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code;
const errorCodes = [
Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0.code,
Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies.code,
Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression.code,
didYouMeanStaticMemberCode,
];
registerCodeFix({
@ -32,7 +32,7 @@ namespace ts.codefix {
function getInfo(sourceFile: SourceFile, pos: number, diagCode: number): Info | undefined {
const node = getTokenAtPosition(sourceFile, pos);
if (isIdentifier(node)) {
if (isIdentifier(node) || isPrivateIdentifier(node)) {
return { node, className: diagCode === didYouMeanStaticMemberCode ? getContainingClass(node)!.name!.text : undefined };
}
}

View file

@ -57,6 +57,10 @@ namespace ts.codefix {
}
suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType);
}
else if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.InKeyword && parent.left === node && isPrivateIdentifier(node)) {
const receiverType = checker.getTypeAtLocation(parent.right);
suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, receiverType);
}
else if (isQualifiedName(parent) && parent.right === node) {
const symbol = checker.getSymbolAtLocation(parent.left);
if (symbol && symbol.flags & SymbolFlags.Module) {

View file

@ -401,6 +401,12 @@ namespace ts {
public kind!: SyntaxKind.PrivateIdentifier;
public escapedText!: __String;
public symbol!: Symbol;
_primaryExpressionBrand: any;
_memberExpressionBrand: any;
_leftHandSideExpressionBrand: any;
_updateExpressionBrand: any;
_unaryExpressionBrand: any;
_expressionBrand: any;
constructor(_kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) {
super(pos, end);
}

View file

@ -670,7 +670,7 @@ declare namespace ts {
readonly parent: Declaration;
readonly expression: Expression;
}
export interface PrivateIdentifier extends Node {
export interface PrivateIdentifier extends PrimaryExpression {
readonly kind: SyntaxKind.PrivateIdentifier;
readonly escapedText: __String;
}

View file

@ -670,7 +670,7 @@ declare namespace ts {
readonly parent: Declaration;
readonly expression: Expression;
}
export interface PrivateIdentifier extends Node {
export interface PrivateIdentifier extends PrimaryExpression {
readonly kind: SyntaxKind.PrivateIdentifier;
readonly escapedText: __String;
}

View file

@ -5,6 +5,7 @@ declare var dec: any;
@dec export class A {
#x: number = 1;
async f() { this.#x = await this.#x; }
g(u) { return #x in u; }
}
const o = { a: 1 };
@ -18,11 +19,12 @@ export declare function __metadata(metadataKey: any, metadataValue: any): Functi
export declare function __awaiter(thisArg: any, _arguments: any, P: Function, generator: Function): any;
export declare function __classPrivateFieldGet(a: any, b: any, c: any, d: any): any;
export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e: any): any;
export declare function __classPrivateFieldIn(a: any, b: any): boolean;
//// [a.js]
var _A_x;
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet, __decorate } from "tslib";
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldIn, __classPrivateFieldSet, __decorate } from "tslib";
let A = class A {
constructor() {
_A_x.set(this, 1);
@ -30,6 +32,7 @@ let A = class A {
f() {
return __awaiter(this, void 0, void 0, function* () { __classPrivateFieldSet(this, _A_x, yield __classPrivateFieldGet(this, _A_x, "f"), "f"); });
}
g(u) { return __classPrivateFieldIn(_A_x, u); }
};
_A_x = new WeakMap();
A = __decorate([

View file

@ -15,15 +15,21 @@ declare var dec: any;
>this : Symbol(A, Decl(a.ts, 0, 21))
>this.#x : Symbol(A.#x, Decl(a.ts, 1, 21))
>this : Symbol(A, Decl(a.ts, 0, 21))
g(u) { return #x in u; }
>g : Symbol(A.g, Decl(a.ts, 3, 42))
>u : Symbol(u, Decl(a.ts, 4, 6))
>#x : Symbol(A.#x, Decl(a.ts, 1, 21))
>u : Symbol(u, Decl(a.ts, 4, 6))
}
const o = { a: 1 };
>o : Symbol(o, Decl(a.ts, 6, 5))
>a : Symbol(a, Decl(a.ts, 6, 11))
>o : Symbol(o, Decl(a.ts, 7, 5))
>a : Symbol(a, Decl(a.ts, 7, 11))
const y = { ...o };
>y : Symbol(y, Decl(a.ts, 7, 5))
>o : Symbol(o, Decl(a.ts, 6, 5))
>y : Symbol(y, Decl(a.ts, 8, 5))
>o : Symbol(o, Decl(a.ts, 7, 5))
=== tests/cases/compiler/tslib.d.ts ===
export declare function __extends(d: Function, b: Function): void;
@ -78,3 +84,8 @@ export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e
>d : Symbol(d, Decl(tslib.d.ts, --, --))
>e : Symbol(e, Decl(tslib.d.ts, --, --))
export declare function __classPrivateFieldIn(a: any, b: any): boolean;
>__classPrivateFieldIn : Symbol(__classPrivateFieldIn, Decl(tslib.d.ts, --, --))
>a : Symbol(a, Decl(tslib.d.ts, --, --))
>b : Symbol(b, Decl(tslib.d.ts, --, --))

View file

@ -18,6 +18,13 @@ declare var dec: any;
>await this.#x : number
>this.#x : number
>this : this
g(u) { return #x in u; }
>g : (u: any) => boolean
>u : any
>#x in u : boolean
>#x : any
>u : any
}
const o = { a: 1 };
@ -76,3 +83,8 @@ export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e
>d : any
>e : any
export declare function __classPrivateFieldIn(a: any, b: any): boolean;
>__classPrivateFieldIn : (a: any, b: any) => boolean
>a : any
>b : any

View file

@ -0,0 +1,23 @@
tests/cases/compiler/main.ts(4,9): error TS2343: This syntax requires an imported helper named '__classPrivateFieldSet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.
tests/cases/compiler/main.ts(4,23): error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.
tests/cases/compiler/main.ts(5,9): error TS2343: This syntax requires an imported helper named '__classPrivateFieldIn' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.
==== tests/cases/compiler/main.ts (3 errors) ====
export class Foo {
#field = true;
f() {
this.#field = this.#field;
~~~~~~~~~~~
!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldSet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.
~~~~~~~~~~~
!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.
#field in this;
~~~~~~
!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldIn' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.
}
}
==== tests/cases/compiler/tslib.d.ts (0 errors) ====
export {}

View file

@ -0,0 +1,32 @@
//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFields.ts] ////
//// [main.ts]
export class Foo {
#field = true;
f() {
this.#field = this.#field;
#field in this;
}
}
//// [tslib.d.ts]
export {}
//// [main.js]
"use strict";
var _Foo_field;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Foo = void 0;
const tslib_1 = require("tslib");
class Foo {
constructor() {
_Foo_field.set(this, true);
}
f() {
(0, tslib_1.__classPrivateFieldSet)(this, _Foo_field, (0, tslib_1.__classPrivateFieldGet)(this, _Foo_field, "f"), "f");
(0, tslib_1.__classPrivateFieldIn)(_Foo_field, this);
}
}
exports.Foo = Foo;
_Foo_field = new WeakMap();

View file

@ -0,0 +1,26 @@
=== tests/cases/compiler/main.ts ===
export class Foo {
>Foo : Symbol(Foo, Decl(main.ts, 0, 0))
#field = true;
>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18))
f() {
>f : Symbol(Foo.f, Decl(main.ts, 1, 18))
this.#field = this.#field;
>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18))
>this : Symbol(Foo, Decl(main.ts, 0, 0))
>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18))
>this : Symbol(Foo, Decl(main.ts, 0, 0))
#field in this;
>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18))
>this : Symbol(Foo, Decl(main.ts, 0, 0))
}
}
=== tests/cases/compiler/tslib.d.ts ===
export {}
No type information for this code.
No type information for this code.

View file

@ -0,0 +1,29 @@
=== tests/cases/compiler/main.ts ===
export class Foo {
>Foo : Foo
#field = true;
>#field : boolean
>true : true
f() {
>f : () => void
this.#field = this.#field;
>this.#field = this.#field : boolean
>this.#field : boolean
>this : this
>this.#field : boolean
>this : this
#field in this;
>#field in this : boolean
>#field : any
>this : this
}
}
=== tests/cases/compiler/tslib.d.ts ===
export {}
No type information for this code.
No type information for this code.

View file

@ -1,12 +1,12 @@
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS2531: Object is possibly 'null'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2532: Object is possibly 'undefined'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(35,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(36,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
@ -17,7 +17,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(42,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,16): error TS2531: Object is possibly 'null'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(44,17): error TS2532: Object is possibly 'undefined'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,17): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
@ -38,13 +38,13 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
var ra1 = a1 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
var ra2 = a2 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
var ra3 = a3 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
var ra4 = a4 in x;
var ra5 = null in x;
~~~~
@ -55,16 +55,16 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
var ra7 = E.a in x;
var ra8 = false in x;
~~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
var ra9 = {} in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
var ra10 = a5 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
var ra11 = a6 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
// invalid right operands
// the right operand is required to be of type Any, an object type, or a type parameter type
@ -108,6 +108,6 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
// both operands are invalid
var rc1 = {} in '';
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.

View file

@ -1,15 +1,12 @@
tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS1127: Invalid character.
tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS2304: Cannot find name '#'.
tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(4,5): error TS1127: Invalid character.
tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(7,14): error TS1127: Invalid character.
==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (4 errors) ====
==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (3 errors) ====
#
~
!!! error TS1127: Invalid character.
~
!!! error TS2304: Cannot find name '#'.
class C {
#

View file

@ -1,6 +1,5 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts ===
#
># : any
class C {
>C : C

View file

@ -0,0 +1,146 @@
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2304: Cannot find name '#fiel'.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(29,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(43,27): error TS2531: Object is possibly 'null'.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies.
==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ====
class Foo {
#field = 1;
static #staticField = 2;
#method() {}
static #staticMethod() {}
goodRhs(v: any) {
const a = #field in v;
const b = #field in v.p1.p2;
const c = #field in (v as {});
const d = #field in (v as Foo);
const e = #field in (v as never);
for (let f in #field in v as any) { /**/ } // unlikely but valid
}
badRhs(v: any) {
const a = #field in (v as unknown); // Bad - RHS of in must be object type or any
~~~~~~~~~~~~~~
!!! error TS2571: Object is of type 'unknown'.
const b = #fiel in v; // Bad - typo in privateID
~~~~~
!!! error TS2304: Cannot find name '#fiel'.
~~~~~
!!! error TS2339: Property '#fiel' does not exist on type 'any'.
const c = (#field) in v; // Bad - privateID is not an expression on its own
~~~~~~
!!! error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression
for (#field in v) { /**/ } // Bad - 'in' not allowed
~~~~~~
!!! error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression
~~~~~~
!!! error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access.
for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any
~~~~~~~~~~~
!!! error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'.
}
whitespace(v: any) {
const a = v && /*0*/#field/*1*/
/*2*/in/*3*/
/*4*/v/*5*/
}
flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) {
if (typeof u === 'object') {
if (#field in n) {
n; // good n is never
}
if (#field in u) {
~
!!! error TS2531: Object is possibly 'null'.
u; // good u is Foo
} else {
u; // good u is object | null
}
if (u !== null) {
if (#field in u) {
u; // good u is Foo
} else {
u; // good u is object
}
if (#method in u) {
u; // good u is Foo
}
if (#staticField in u) {
u; // good u is typeof Foo
}
if (#staticMethod in u) {
u; // good u is typeof Foo
}
}
}
if (#field in fb) {
fb; // good fb is Foo
} else {
fb; // good fb is Bar
}
if (#field in fs) {
fs; // good fs is FooSub
} else {
fs; // good fs is never
}
if (#field in b) {
b; // good b is 'Bar & Foo'
} else {
b; // good b is Bar
}
if (#field in fsb) {
fsb; // good fsb is FooSub
} else {
fsb; // good fsb is Bar
}
if (#field in fsfb) {
fsfb; // good fsfb is 'Foo | FooSub'
} else {
fsfb; // good fsfb is Bar
}
class Nested {
m(v: any) {
if (#field in v) {
v; // good v is Foo
}
}
}
}
}
class FooSub extends Foo { subTypeOfFoo = true }
class Bar { notFoo = true }
function badSyntax(v: Foo) {
return #field in v; // Bad - outside of class
~~~~~~
!!! error TS18016: Private identifiers are not allowed outside class bodies.
}

View file

@ -0,0 +1,222 @@
//// [privateNameInInExpression.ts]
class Foo {
#field = 1;
static #staticField = 2;
#method() {}
static #staticMethod() {}
goodRhs(v: any) {
const a = #field in v;
const b = #field in v.p1.p2;
const c = #field in (v as {});
const d = #field in (v as Foo);
const e = #field in (v as never);
for (let f in #field in v as any) { /**/ } // unlikely but valid
}
badRhs(v: any) {
const a = #field in (v as unknown); // Bad - RHS of in must be object type or any
const b = #fiel in v; // Bad - typo in privateID
const c = (#field) in v; // Bad - privateID is not an expression on its own
for (#field in v) { /**/ } // Bad - 'in' not allowed
for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any
}
whitespace(v: any) {
const a = v && /*0*/#field/*1*/
/*2*/in/*3*/
/*4*/v/*5*/
}
flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) {
if (typeof u === 'object') {
if (#field in n) {
n; // good n is never
}
if (#field in u) {
u; // good u is Foo
} else {
u; // good u is object | null
}
if (u !== null) {
if (#field in u) {
u; // good u is Foo
} else {
u; // good u is object
}
if (#method in u) {
u; // good u is Foo
}
if (#staticField in u) {
u; // good u is typeof Foo
}
if (#staticMethod in u) {
u; // good u is typeof Foo
}
}
}
if (#field in fb) {
fb; // good fb is Foo
} else {
fb; // good fb is Bar
}
if (#field in fs) {
fs; // good fs is FooSub
} else {
fs; // good fs is never
}
if (#field in b) {
b; // good b is 'Bar & Foo'
} else {
b; // good b is Bar
}
if (#field in fsb) {
fsb; // good fsb is FooSub
} else {
fsb; // good fsb is Bar
}
if (#field in fsfb) {
fsfb; // good fsfb is 'Foo | FooSub'
} else {
fsfb; // good fsfb is Bar
}
class Nested {
m(v: any) {
if (#field in v) {
v; // good v is Foo
}
}
}
}
}
class FooSub extends Foo { subTypeOfFoo = true }
class Bar { notFoo = true }
function badSyntax(v: Foo) {
return #field in v; // Bad - outside of class
}
//// [privateNameInInExpression.js]
"use strict";
class Foo {
#field = 1;
static #staticField = 2;
#method() { }
static #staticMethod() { }
goodRhs(v) {
const a = #field in v;
const b = #field in v.p1.p2;
const c = #field in v;
const d = #field in v;
const e = #field in v;
for (let f in #field in v) { /**/ } // unlikely but valid
}
badRhs(v) {
const a = #field in v; // Bad - RHS of in must be object type or any
const b = #fiel in v; // Bad - typo in privateID
const c = (#field) in v; // Bad - privateID is not an expression on its own
for (#field in v) { /**/ } // Bad - 'in' not allowed
for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any
}
whitespace(v) {
const a = v && /*0*/ #field /*1*/
/*2*/ in /*3*/
/*4*/ v; /*5*/
}
flow(u, n, fb, fs, b, fsb, fsfb) {
if (typeof u === 'object') {
if (#field in n) {
n; // good n is never
}
if (#field in u) {
u; // good u is Foo
}
else {
u; // good u is object | null
}
if (u !== null) {
if (#field in u) {
u; // good u is Foo
}
else {
u; // good u is object
}
if (#method in u) {
u; // good u is Foo
}
if (#staticField in u) {
u; // good u is typeof Foo
}
if (#staticMethod in u) {
u; // good u is typeof Foo
}
}
}
if (#field in fb) {
fb; // good fb is Foo
}
else {
fb; // good fb is Bar
}
if (#field in fs) {
fs; // good fs is FooSub
}
else {
fs; // good fs is never
}
if (#field in b) {
b; // good b is 'Bar & Foo'
}
else {
b; // good b is Bar
}
if (#field in fsb) {
fsb; // good fsb is FooSub
}
else {
fsb; // good fsb is Bar
}
if (#field in fsfb) {
fsfb; // good fsfb is 'Foo | FooSub'
}
else {
fsfb; // good fsfb is Bar
}
class Nested {
m(v) {
if (#field in v) {
v; // good v is Foo
}
}
}
}
}
class FooSub extends Foo {
subTypeOfFoo = true;
}
class Bar {
notFoo = true;
}
function badSyntax(v) {
return #field in v; // Bad - outside of class
}

View file

@ -0,0 +1,269 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts ===
class Foo {
>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0))
#field = 1;
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
static #staticField = 2;
>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpression.ts, 1, 15))
#method() {}
>#method : Symbol(Foo.#method, Decl(privateNameInInExpression.ts, 2, 28))
static #staticMethod() {}
>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 3, 16))
goodRhs(v: any) {
>goodRhs : Symbol(Foo.goodRhs, Decl(privateNameInInExpression.ts, 4, 29))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12))
const a = #field in v;
>a : Symbol(a, Decl(privateNameInInExpression.ts, 7, 13))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12))
const b = #field in v.p1.p2;
>b : Symbol(b, Decl(privateNameInInExpression.ts, 9, 13))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12))
const c = #field in (v as {});
>c : Symbol(c, Decl(privateNameInInExpression.ts, 11, 13))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12))
const d = #field in (v as Foo);
>d : Symbol(d, Decl(privateNameInInExpression.ts, 13, 13))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12))
>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0))
const e = #field in (v as never);
>e : Symbol(e, Decl(privateNameInInExpression.ts, 15, 13))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12))
for (let f in #field in v as any) { /**/ } // unlikely but valid
>f : Symbol(f, Decl(privateNameInInExpression.ts, 17, 16))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12))
}
badRhs(v: any) {
>badRhs : Symbol(Foo.badRhs, Decl(privateNameInInExpression.ts, 18, 5))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11))
const a = #field in (v as unknown); // Bad - RHS of in must be object type or any
>a : Symbol(a, Decl(privateNameInInExpression.ts, 20, 13))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11))
const b = #fiel in v; // Bad - typo in privateID
>b : Symbol(b, Decl(privateNameInInExpression.ts, 22, 13))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11))
const c = (#field) in v; // Bad - privateID is not an expression on its own
>c : Symbol(c, Decl(privateNameInInExpression.ts, 24, 13))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11))
for (#field in v) { /**/ } // Bad - 'in' not allowed
>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11))
for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any
>d : Symbol(d, Decl(privateNameInInExpression.ts, 28, 16))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11))
}
whitespace(v: any) {
>whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 29, 5))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15))
const a = v && /*0*/#field/*1*/
>a : Symbol(a, Decl(privateNameInInExpression.ts, 31, 13))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
/*2*/in/*3*/
/*4*/v/*5*/
>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15))
}
flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) {
>flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 34, 5))
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20))
>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30))
>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0))
>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48))
>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45))
>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1))
>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57))
>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48))
>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65))
>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1))
>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48))
>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84))
>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0))
>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1))
>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48))
if (typeof u === 'object') {
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
if (#field in n) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20))
n; // good n is never
>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20))
}
if (#field in u) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
u; // good u is Foo
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
} else {
u; // good u is object | null
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
}
if (u !== null) {
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
if (#field in u) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
u; // good u is Foo
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
} else {
u; // good u is object
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
}
if (#method in u) {
>#method : Symbol(Foo.#method, Decl(privateNameInInExpression.ts, 2, 28))
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
u; // good u is Foo
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
}
if (#staticField in u) {
>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpression.ts, 1, 15))
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
u; // good u is typeof Foo
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
}
if (#staticMethod in u) {
>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 3, 16))
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
u; // good u is typeof Foo
>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9))
}
}
}
if (#field in fb) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30))
fb; // good fb is Foo
>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30))
} else {
fb; // good fb is Bar
>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30))
}
if (#field in fs) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45))
fs; // good fs is FooSub
>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45))
} else {
fs; // good fs is never
>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45))
}
if (#field in b) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57))
b; // good b is 'Bar & Foo'
>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57))
} else {
b; // good b is Bar
>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57))
}
if (#field in fsb) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65))
fsb; // good fsb is FooSub
>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65))
} else {
fsb; // good fsb is Bar
>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65))
}
if (#field in fsfb) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84))
fsfb; // good fsfb is 'Foo | FooSub'
>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84))
} else {
fsfb; // good fsfb is Bar
>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84))
}
class Nested {
>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 97, 9))
m(v: any) {
>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 99, 22))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14))
if (#field in v) {
>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14))
v; // good v is Foo
>v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14))
}
}
}
}
}
class FooSub extends Foo { subTypeOfFoo = true }
>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1))
>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0))
>subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 109, 26))
class Bar { notFoo = true }
>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48))
>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 110, 11))
function badSyntax(v: Foo) {
>badSyntax : Symbol(badSyntax, Decl(privateNameInInExpression.ts, 110, 27))
>v : Symbol(v, Decl(privateNameInInExpression.ts, 112, 19))
>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0))
return #field in v; // Bad - outside of class
>v : Symbol(v, Decl(privateNameInInExpression.ts, 112, 19))
}

View file

@ -0,0 +1,308 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts ===
class Foo {
>Foo : Foo
#field = 1;
>#field : number
>1 : 1
static #staticField = 2;
>#staticField : number
>2 : 2
#method() {}
>#method : () => void
static #staticMethod() {}
>#staticMethod : () => void
goodRhs(v: any) {
>goodRhs : (v: any) => void
>v : any
const a = #field in v;
>a : boolean
>#field in v : boolean
>#field : any
>v : any
const b = #field in v.p1.p2;
>b : boolean
>#field in v.p1.p2 : boolean
>#field : any
>v.p1.p2 : any
>v.p1 : any
>v : any
>p1 : any
>p2 : any
const c = #field in (v as {});
>c : boolean
>#field in (v as {}) : boolean
>#field : any
>(v as {}) : {}
>v as {} : {}
>v : any
const d = #field in (v as Foo);
>d : boolean
>#field in (v as Foo) : boolean
>#field : any
>(v as Foo) : Foo
>v as Foo : Foo
>v : any
const e = #field in (v as never);
>e : boolean
>#field in (v as never) : boolean
>#field : any
>(v as never) : never
>v as never : never
>v : any
for (let f in #field in v as any) { /**/ } // unlikely but valid
>f : string
>#field in v as any : any
>#field in v : boolean
>#field : any
>v : any
}
badRhs(v: any) {
>badRhs : (v: any) => void
>v : any
const a = #field in (v as unknown); // Bad - RHS of in must be object type or any
>a : boolean
>#field in (v as unknown) : boolean
>#field : any
>(v as unknown) : unknown
>v as unknown : unknown
>v : any
const b = #fiel in v; // Bad - typo in privateID
>b : boolean
>#fiel in v : boolean
>#fiel : any
>v : any
const c = (#field) in v; // Bad - privateID is not an expression on its own
>c : boolean
>(#field) in v : boolean
>(#field) : any
>v : any
for (#field in v) { /**/ } // Bad - 'in' not allowed
>v : any
for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any
>d : string
>#field in v : boolean
>#field : any
>v : any
}
whitespace(v: any) {
>whitespace : (v: any) => void
>v : any
const a = v && /*0*/#field/*1*/
>a : any
>v && /*0*/#field/*1*/ /*2*/in/*3*/ /*4*/v : any
>v : any
>#field/*1*/ /*2*/in/*3*/ /*4*/v : boolean
>#field : any
/*2*/in/*3*/
/*4*/v/*5*/
>v : any
}
flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) {
>flow : (u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) => void
>u : unknown
>n : never
>fb : Foo | Bar
>fs : FooSub
>b : Bar
>fsb : Bar | FooSub
>fsfb : Foo | Bar | FooSub
if (typeof u === 'object') {
>typeof u === 'object' : boolean
>typeof u : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>u : unknown
>'object' : "object"
if (#field in n) {
>#field in n : boolean
>#field : any
>n : never
n; // good n is never
>n : never
}
if (#field in u) {
>#field in u : boolean
>#field : any
>u : object | null
u; // good u is Foo
>u : Foo
} else {
u; // good u is object | null
>u : object | null
}
if (u !== null) {
>u !== null : boolean
>u : object | null
>null : null
if (#field in u) {
>#field in u : boolean
>#field : any
>u : object
u; // good u is Foo
>u : Foo
} else {
u; // good u is object
>u : object
}
if (#method in u) {
>#method in u : boolean
>#method : any
>u : object
u; // good u is Foo
>u : Foo
}
if (#staticField in u) {
>#staticField in u : boolean
>#staticField : any
>u : object
u; // good u is typeof Foo
>u : typeof Foo
}
if (#staticMethod in u) {
>#staticMethod in u : boolean
>#staticMethod : any
>u : object
u; // good u is typeof Foo
>u : typeof Foo
}
}
}
if (#field in fb) {
>#field in fb : boolean
>#field : any
>fb : Foo | Bar
fb; // good fb is Foo
>fb : Foo
} else {
fb; // good fb is Bar
>fb : Bar
}
if (#field in fs) {
>#field in fs : boolean
>#field : any
>fs : FooSub
fs; // good fs is FooSub
>fs : FooSub
} else {
fs; // good fs is never
>fs : never
}
if (#field in b) {
>#field in b : boolean
>#field : any
>b : Bar
b; // good b is 'Bar & Foo'
>b : Bar & Foo
} else {
b; // good b is Bar
>b : Bar
}
if (#field in fsb) {
>#field in fsb : boolean
>#field : any
>fsb : Bar | FooSub
fsb; // good fsb is FooSub
>fsb : FooSub
} else {
fsb; // good fsb is Bar
>fsb : Bar
}
if (#field in fsfb) {
>#field in fsfb : boolean
>#field : any
>fsfb : Foo | Bar | FooSub
fsfb; // good fsfb is 'Foo | FooSub'
>fsfb : Foo | FooSub
} else {
fsfb; // good fsfb is Bar
>fsfb : Bar
}
class Nested {
>Nested : Nested
m(v: any) {
>m : (v: any) => void
>v : any
if (#field in v) {
>#field in v : boolean
>#field : any
>v : any
v; // good v is Foo
>v : Foo
}
}
}
}
}
class FooSub extends Foo { subTypeOfFoo = true }
>FooSub : FooSub
>Foo : Foo
>subTypeOfFoo : boolean
>true : true
class Bar { notFoo = true }
>Bar : Bar
>notFoo : boolean
>true : true
function badSyntax(v: Foo) {
>badSyntax : (v: Foo) => boolean
>v : Foo
return #field in v; // Bad - outside of class
>#field in v : boolean
>#field : any
>v : Foo
}

View file

@ -0,0 +1,61 @@
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected.
==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (4 errors) ====
class Foo {
#field = 1;
#method() {}
static #staticField= 2;
static #staticMethod() {}
check(v: any) {
#field in v; // expect Foo's 'field' WeakMap
#method in v; // expect Foo's 'instances' WeakSet
#staticField in v; // expect Foo's constructor
#staticMethod in v; // expect Foo's constructor
}
precedence(v: any) {
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
~~~~~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
~~~~~~~~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
}
invalidLHS(v: any) {
'prop' in v = 10;
~
!!! error TS1005: ';' expected.
#field in v = 10;
~
!!! error TS1005: ';' expected.
}
}
class Bar {
#field = 1;
check(v: any) {
#field in v; // expect Bar's 'field' WeakMap
}
}
function syntaxError(v: Foo) {
return #field in v; // expect `return in v` so runtime will have a syntax error
}
export { }

View file

@ -0,0 +1,97 @@
//// [privateNameInInExpressionTransform.ts]
class Foo {
#field = 1;
#method() {}
static #staticField= 2;
static #staticMethod() {}
check(v: any) {
#field in v; // expect Foo's 'field' WeakMap
#method in v; // expect Foo's 'instances' WeakSet
#staticField in v; // expect Foo's constructor
#staticMethod in v; // expect Foo's constructor
}
precedence(v: any) {
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
}
invalidLHS(v: any) {
'prop' in v = 10;
#field in v = 10;
}
}
class Bar {
#field = 1;
check(v: any) {
#field in v; // expect Bar's 'field' WeakMap
}
}
function syntaxError(v: Foo) {
return #field in v; // expect `return in v` so runtime will have a syntax error
}
export { }
//// [privateNameInInExpressionTransform.js]
var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(state, receiver) {
if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object");
return typeof state === "function" ? receiver === state : state.has(receiver);
};
var _Foo_instances, _a, _Foo_field, _Foo_method, _Foo_staticField, _Foo_staticMethod, _Bar_field;
class Foo {
constructor() {
_Foo_instances.add(this);
_Foo_field.set(this, 1);
}
check(v) {
__classPrivateFieldIn(_Foo_field, v); // expect Foo's 'field' WeakMap
__classPrivateFieldIn(_Foo_instances, v); // expect Foo's 'instances' WeakSet
__classPrivateFieldIn(_a, v); // expect Foo's constructor
__classPrivateFieldIn(_a, v); // expect Foo's constructor
}
precedence(v) {
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == __classPrivateFieldIn(_Foo_field, v) || v; // Good precedence: (v == (#field in v)) || v
v << in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
v << in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
v == __classPrivateFieldIn(_Foo_field, v) in v; // Good precedence: v == ((#field in v) in v)
__classPrivateFieldIn(_Foo_field, v) && __classPrivateFieldIn(_Foo_field, v); // Good precedence: (#field in v) && (#field in v)
}
invalidLHS(v) {
'prop' in v;
10;
__classPrivateFieldIn(_Foo_field, v);
10;
}
}
_a = Foo, _Foo_field = new WeakMap(), _Foo_instances = new WeakSet(), _Foo_method = function _Foo_method() { }, _Foo_staticMethod = function _Foo_staticMethod() { };
_Foo_staticField = { value: 2 };
class Bar {
constructor() {
_Bar_field.set(this, 1);
}
check(v) {
__classPrivateFieldIn(_Bar_field, v); // expect Bar's 'field' WeakMap
}
}
_Bar_field = new WeakMap();
function syntaxError(v) {
return in v; // expect `return in v` so runtime will have a syntax error
}
export {};

View file

@ -0,0 +1,112 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts ===
class Foo {
>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0))
#field = 1;
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
#method() {}
>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15))
static #staticField= 2;
>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16))
static #staticMethod() {}
>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27))
check(v: any) {
>check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 4, 29))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#field in v; // expect Foo's 'field' WeakMap
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#method in v; // expect Foo's 'instances' WeakSet
>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#staticField in v; // expect Foo's constructor
>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#staticMethod in v; // expect Foo's constructor
>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
}
precedence(v: any) {
>precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 11, 5))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
}
invalidLHS(v: any) {
>invalidLHS : Symbol(Foo.invalidLHS, Decl(privateNameInInExpressionTransform.ts, 26, 5))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15))
'prop' in v = 10;
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15))
#field in v = 10;
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15))
}
}
class Bar {
>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 31, 1))
#field = 1;
>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11))
check(v: any) {
>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 34, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10))
#field in v; // expect Bar's 'field' WeakMap
>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10))
}
}
function syntaxError(v: Foo) {
>syntaxError : Symbol(syntaxError, Decl(privateNameInInExpressionTransform.ts, 38, 1))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21))
>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0))
return #field in v; // expect `return in v` so runtime will have a syntax error
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21))
}
export { }

View file

@ -0,0 +1,141 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts ===
class Foo {
>Foo : Foo
#field = 1;
>#field : number
>1 : 1
#method() {}
>#method : () => void
static #staticField= 2;
>#staticField : number
>2 : 2
static #staticMethod() {}
>#staticMethod : () => void
check(v: any) {
>check : (v: any) => void
>v : any
#field in v; // expect Foo's 'field' WeakMap
>#field in v : boolean
>#field : any
>v : any
#method in v; // expect Foo's 'instances' WeakSet
>#method in v : boolean
>#method : any
>v : any
#staticField in v; // expect Foo's constructor
>#staticField in v : boolean
>#staticField : any
>v : any
#staticMethod in v; // expect Foo's constructor
>#staticMethod in v : boolean
>#staticMethod : any
>v : any
}
precedence(v: any) {
>precedence : (v: any) => void
>v : any
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
>v == #field in v || v : any
>v == #field in v : boolean
>v : any
>#field in v : boolean
>#field : any
>v : any
>v : any
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
>v << #field in v << v : boolean
>v << #field : number
>v : any
>v << v : number
>v : any
>v : any
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
>v << #field in v == v : boolean
>v << #field in v : boolean
>v << #field : number
>v : any
>v : any
>v : any
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
>v == #field in v in v : boolean
>v : any
>#field in v in v : boolean
>#field in v : boolean
>#field : any
>v : any
>v : any
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
>#field in v && #field in v : boolean
>#field in v : boolean
>#field : any
>v : any
>#field in v : boolean
>#field : any
>v : Foo
}
invalidLHS(v: any) {
>invalidLHS : (v: any) => void
>v : any
'prop' in v = 10;
>'prop' in v : boolean
>'prop' : "prop"
>v : any
>10 : 10
#field in v = 10;
>#field in v : boolean
>#field : any
>v : any
>10 : 10
}
}
class Bar {
>Bar : Bar
#field = 1;
>#field : number
>1 : 1
check(v: any) {
>check : (v: any) => void
>v : any
#field in v; // expect Bar's 'field' WeakMap
>#field in v : boolean
>#field : any
>v : any
}
}
function syntaxError(v: Foo) {
>syntaxError : (v: Foo) => boolean
>v : Foo
return #field in v; // expect `return in v` so runtime will have a syntax error
>#field in v : boolean
>#field : any
>v : Foo
}
export { }

View file

@ -0,0 +1,64 @@
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(4,26): error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected.
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected.
==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (5 errors) ====
class Foo {
#field = 1;
#method() {}
static #staticField= 2;
~
!!! error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag.
static #staticMethod() {}
check(v: any) {
#field in v; // expect Foo's 'field' WeakMap
#method in v; // expect Foo's 'instances' WeakSet
#staticField in v; // expect Foo's constructor
#staticMethod in v; // expect Foo's constructor
}
precedence(v: any) {
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
~~~~~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
~~~~~~~~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
}
invalidLHS(v: any) {
'prop' in v = 10;
~
!!! error TS1005: ';' expected.
#field in v = 10;
~
!!! error TS1005: ';' expected.
}
}
class Bar {
#field = 1;
check(v: any) {
#field in v; // expect Bar's 'field' WeakMap
}
}
function syntaxError(v: Foo) {
return #field in v; // expect `return in v` so runtime will have a syntax error
}
export { }

View file

@ -0,0 +1,87 @@
//// [privateNameInInExpressionTransform.ts]
class Foo {
#field = 1;
#method() {}
static #staticField= 2;
static #staticMethod() {}
check(v: any) {
#field in v; // expect Foo's 'field' WeakMap
#method in v; // expect Foo's 'instances' WeakSet
#staticField in v; // expect Foo's constructor
#staticMethod in v; // expect Foo's constructor
}
precedence(v: any) {
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
}
invalidLHS(v: any) {
'prop' in v = 10;
#field in v = 10;
}
}
class Bar {
#field = 1;
check(v: any) {
#field in v; // expect Bar's 'field' WeakMap
}
}
function syntaxError(v: Foo) {
return #field in v; // expect `return in v` so runtime will have a syntax error
}
export { }
//// [privateNameInInExpressionTransform.js]
class Foo {
#field = 1;
#method() { }
static #staticField = 2;
static #staticMethod() { }
check(v) {
#field in v; // expect Foo's 'field' WeakMap
#method in v; // expect Foo's 'instances' WeakSet
#staticField in v; // expect Foo's constructor
#staticMethod in v; // expect Foo's constructor
}
precedence(v) {
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
}
invalidLHS(v) {
'prop' in v;
10;
#field in v;
10;
}
}
class Bar {
#field = 1;
check(v) {
#field in v; // expect Bar's 'field' WeakMap
}
}
function syntaxError(v) {
return #field in v; // expect `return in v` so runtime will have a syntax error
}
export {};

View file

@ -0,0 +1,112 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts ===
class Foo {
>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0))
#field = 1;
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
#method() {}
>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15))
static #staticField= 2;
>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16))
static #staticMethod() {}
>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27))
check(v: any) {
>check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 4, 29))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#field in v; // expect Foo's 'field' WeakMap
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#method in v; // expect Foo's 'instances' WeakSet
>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#staticField in v; // expect Foo's constructor
>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
#staticMethod in v; // expect Foo's constructor
>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10))
}
precedence(v: any) {
>precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 11, 5))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15))
}
invalidLHS(v: any) {
>invalidLHS : Symbol(Foo.invalidLHS, Decl(privateNameInInExpressionTransform.ts, 26, 5))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15))
'prop' in v = 10;
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15))
#field in v = 10;
>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15))
}
}
class Bar {
>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 31, 1))
#field = 1;
>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11))
check(v: any) {
>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 34, 15))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10))
#field in v; // expect Bar's 'field' WeakMap
>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10))
}
}
function syntaxError(v: Foo) {
>syntaxError : Symbol(syntaxError, Decl(privateNameInInExpressionTransform.ts, 38, 1))
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21))
>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0))
return #field in v; // expect `return in v` so runtime will have a syntax error
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21))
}
export { }

View file

@ -0,0 +1,141 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts ===
class Foo {
>Foo : Foo
#field = 1;
>#field : number
>1 : 1
#method() {}
>#method : () => void
static #staticField= 2;
>#staticField : number
>2 : 2
static #staticMethod() {}
>#staticMethod : () => void
check(v: any) {
>check : (v: any) => void
>v : any
#field in v; // expect Foo's 'field' WeakMap
>#field in v : boolean
>#field : any
>v : any
#method in v; // expect Foo's 'instances' WeakSet
>#method in v : boolean
>#method : any
>v : any
#staticField in v; // expect Foo's constructor
>#staticField in v : boolean
>#staticField : any
>v : any
#staticMethod in v; // expect Foo's constructor
>#staticMethod in v : boolean
>#staticMethod : any
>v : any
}
precedence(v: any) {
>precedence : (v: any) => void
>v : any
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
>v == #field in v || v : any
>v == #field in v : boolean
>v : any
>#field in v : boolean
>#field : any
>v : any
>v : any
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
>v << #field in v << v : boolean
>v << #field : number
>v : any
>v << v : number
>v : any
>v : any
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
>v << #field in v == v : boolean
>v << #field in v : boolean
>v << #field : number
>v : any
>v : any
>v : any
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
>v == #field in v in v : boolean
>v : any
>#field in v in v : boolean
>#field in v : boolean
>#field : any
>v : any
>v : any
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
>#field in v && #field in v : boolean
>#field in v : boolean
>#field : any
>v : any
>#field in v : boolean
>#field : any
>v : Foo
}
invalidLHS(v: any) {
>invalidLHS : (v: any) => void
>v : any
'prop' in v = 10;
>'prop' in v : boolean
>'prop' : "prop"
>v : any
>10 : 10
#field in v = 10;
>#field in v : boolean
>#field : any
>v : any
>10 : 10
}
}
class Bar {
>Bar : Bar
#field = 1;
>#field : number
>1 : 1
check(v: any) {
>check : (v: any) => void
>v : any
#field in v; // expect Bar's 'field' WeakMap
>#field in v : boolean
>#field : any
>v : any
}
}
function syntaxError(v: Foo) {
>syntaxError : (v: Foo) => boolean
>v : Foo
return #field in v; // expect `return in v` so runtime will have a syntax error
>#field in v : boolean
>#field : any
>v : Foo
}
export { }

View file

@ -0,0 +1,16 @@
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts(2,5): error TS6133: '#unused' is declared but its value is never read.
==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts (1 errors) ====
class Foo {
#unused: undefined; // expect unused error
~~~~~~~
!!! error TS6133: '#unused' is declared but its value is never read.
#brand: undefined; // expect no error
isFoo(v: any): v is Foo {
// This should count as using/reading '#brand'
return #brand in v;
}
}

View file

@ -0,0 +1,22 @@
//// [privateNameInInExpressionUnused.ts]
class Foo {
#unused: undefined; // expect unused error
#brand: undefined; // expect no error
isFoo(v: any): v is Foo {
// This should count as using/reading '#brand'
return #brand in v;
}
}
//// [privateNameInInExpressionUnused.js]
"use strict";
class Foo {
#unused; // expect unused error
#brand; // expect no error
isFoo(v) {
// This should count as using/reading '#brand'
return #brand in v;
}
}

View file

@ -0,0 +1,23 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts ===
class Foo {
>Foo : Symbol(Foo, Decl(privateNameInInExpressionUnused.ts, 0, 0))
#unused: undefined; // expect unused error
>#unused : Symbol(Foo.#unused, Decl(privateNameInInExpressionUnused.ts, 0, 11))
#brand: undefined; // expect no error
>#brand : Symbol(Foo.#brand, Decl(privateNameInInExpressionUnused.ts, 1, 23))
isFoo(v: any): v is Foo {
>isFoo : Symbol(Foo.isFoo, Decl(privateNameInInExpressionUnused.ts, 2, 22))
>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10))
>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10))
>Foo : Symbol(Foo, Decl(privateNameInInExpressionUnused.ts, 0, 0))
// This should count as using/reading '#brand'
return #brand in v;
>#brand : Symbol(Foo.#brand, Decl(privateNameInInExpressionUnused.ts, 1, 23))
>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10))
}
}

View file

@ -0,0 +1,22 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts ===
class Foo {
>Foo : Foo
#unused: undefined; // expect unused error
>#unused : undefined
#brand: undefined; // expect no error
>#brand : undefined
isFoo(v: any): v is Foo {
>isFoo : (v: any) => v is Foo
>v : any
// This should count as using/reading '#brand'
return #brand in v;
>#brand in v : boolean
>#brand : any
>v : any
}
}

View file

@ -6,6 +6,7 @@ declare var dec: any;
@dec export class A {
#x: number = 1;
async f() { this.#x = await this.#x; }
g(u) { return #x in u; }
}
const o = { a: 1 };
@ -19,3 +20,4 @@ export declare function __metadata(metadataKey: any, metadataValue: any): Functi
export declare function __awaiter(thisArg: any, _arguments: any, P: Function, generator: Function): any;
export declare function __classPrivateFieldGet(a: any, b: any, c: any, d: any): any;
export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e: any): any;
export declare function __classPrivateFieldIn(a: any, b: any): boolean;

View file

@ -0,0 +1,16 @@
// @importHelpers: true
// @target: es2020
// @module: commonjs
// @lib: esnext
// @moduleResolution: classic
// @filename: main.ts
export class Foo {
#field = true;
f() {
this.#field = this.#field;
#field in this;
}
}
// @filename: tslib.d.ts
export {}

View file

@ -0,0 +1,119 @@
// @strict: true
// @target: esnext
// @useDefineForClassFields: true
class Foo {
#field = 1;
static #staticField = 2;
#method() {}
static #staticMethod() {}
goodRhs(v: any) {
const a = #field in v;
const b = #field in v.p1.p2;
const c = #field in (v as {});
const d = #field in (v as Foo);
const e = #field in (v as never);
for (let f in #field in v as any) { /**/ } // unlikely but valid
}
badRhs(v: any) {
const a = #field in (v as unknown); // Bad - RHS of in must be object type or any
const b = #fiel in v; // Bad - typo in privateID
const c = (#field) in v; // Bad - privateID is not an expression on its own
for (#field in v) { /**/ } // Bad - 'in' not allowed
for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any
}
whitespace(v: any) {
const a = v && /*0*/#field/*1*/
/*2*/in/*3*/
/*4*/v/*5*/
}
flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) {
if (typeof u === 'object') {
if (#field in n) {
n; // good n is never
}
if (#field in u) {
u; // good u is Foo
} else {
u; // good u is object | null
}
if (u !== null) {
if (#field in u) {
u; // good u is Foo
} else {
u; // good u is object
}
if (#method in u) {
u; // good u is Foo
}
if (#staticField in u) {
u; // good u is typeof Foo
}
if (#staticMethod in u) {
u; // good u is typeof Foo
}
}
}
if (#field in fb) {
fb; // good fb is Foo
} else {
fb; // good fb is Bar
}
if (#field in fs) {
fs; // good fs is FooSub
} else {
fs; // good fs is never
}
if (#field in b) {
b; // good b is 'Bar & Foo'
} else {
b; // good b is Bar
}
if (#field in fsb) {
fsb; // good fsb is FooSub
} else {
fsb; // good fsb is Bar
}
if (#field in fsfb) {
fsfb; // good fsfb is 'Foo | FooSub'
} else {
fsfb; // good fsfb is Bar
}
class Nested {
m(v: any) {
if (#field in v) {
v; // good v is Foo
}
}
}
}
}
class FooSub extends Foo { subTypeOfFoo = true }
class Bar { notFoo = true }
function badSyntax(v: Foo) {
return #field in v; // Bad - outside of class
}

View file

@ -0,0 +1,47 @@
// @target: esnext, es2020
class Foo {
#field = 1;
#method() {}
static #staticField= 2;
static #staticMethod() {}
check(v: any) {
#field in v; // expect Foo's 'field' WeakMap
#method in v; // expect Foo's 'instances' WeakSet
#staticField in v; // expect Foo's constructor
#staticMethod in v; // expect Foo's constructor
}
precedence(v: any) {
// '==' and '||' have lower precedence than 'in'
// 'in' naturally has same precedence as 'in'
// '<<' has higher precedence than 'in'
v == #field in v || v; // Good precedence: (v == (#field in v)) || v
v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v)
v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v
v == #field in v in v; // Good precedence: v == ((#field in v) in v)
#field in v && #field in v; // Good precedence: (#field in v) && (#field in v)
}
invalidLHS(v: any) {
'prop' in v = 10;
#field in v = 10;
}
}
class Bar {
#field = 1;
check(v: any) {
#field in v; // expect Bar's 'field' WeakMap
}
}
function syntaxError(v: Foo) {
return #field in v; // expect `return in v` so runtime will have a syntax error
}
export { }

View file

@ -0,0 +1,13 @@
// @strict: true
// @noUnusedLocals: true
// @target: esnext
class Foo {
#unused: undefined; // expect unused error
#brand: undefined; // expect no error
isFoo(v: any): v is Foo {
// This should count as using/reading '#brand'
return #brand in v;
}
}

View file

@ -0,0 +1,19 @@
/// <reference path='fourslash.ts' />
////class A {
//// #foo: number;
//// static isA(v: A) {
//// [|return #fo in v;|]
//// }
////}
verify.codeFixAvailable([
{ description: "Change spelling to '#foo'" },
{ description: "Remove unused declaration for: '#foo'" },
]);
verify.codeFix({
index: 0,
description: "Change spelling to '#foo'",
newRangeContent: "return #foo in v;"
});

View file

@ -4,6 +4,7 @@
//// [|[|{|"isDefinition": true, "isWriteAccess": true, "contextRangeIndex": 0 |}#foo|] = 10;|]
//// constructor() {
//// this.[|{|"isWriteAccess": true|}#foo|] = 20;
//// [|#foo|] in this;
//// }
////}
////class D extends C {
@ -13,12 +14,12 @@
//// }
////}
////class E {
//// [|[|{|"isDefinition": true, "contextRangeIndex": 3 |}#foo|]: number;|]
//// [|[|{|"isDefinition": true, "contextRangeIndex": 4 |}#foo|]: number;|]
//// constructor() {
//// this.[|{|"isWriteAccess": true|}#foo|] = 20;
//// }
////}
const [rC0Def, rC0, rC1, rE0Def, rE0, rE1] = test.ranges();
verify.singleReferenceGroup("(property) C.#foo: number", [rC0, rC1]);
const [rC0Def, rC0, rC1, rC2, rE0Def, rE0, rE1] = test.ranges();
verify.singleReferenceGroup("(property) C.#foo: number", [rC0, rC1, rC2]);
verify.singleReferenceGroup("(property) E.#foo: number", [rE0, rE1]);