From 306397bdf11c167428b58e3d5ba0ab21c20a7027 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 18 Feb 2021 20:43:20 +0000 Subject: [PATCH 01/29] basic parsing of private-id-in-in Signed-off-by: Ashley Claymore --- src/compiler/emitter.ts | 15 +++ src/compiler/factory/nodeFactory.ts | 26 ++++ src/compiler/factory/nodeTests.ts | 4 + src/compiler/parser.ts | 20 ++- src/compiler/types.ts | 9 ++ src/compiler/utilitiesPublic.ts | 1 + .../privateNameInInExpression.errors.txt | 68 ++++++++++ .../reference/privateNameInInExpression.js | 111 ++++++++++++++++ .../privateNameInInExpression.symbols | 110 ++++++++++++++++ .../reference/privateNameInInExpression.types | 122 ++++++++++++++++++ .../privateNames/privateNameInInExpression.ts | 58 +++++++++ 11 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/privateNameInInExpression.errors.txt create mode 100644 tests/baselines/reference/privateNameInInExpression.js create mode 100644 tests/baselines/reference/privateNameInInExpression.symbols create mode 100644 tests/baselines/reference/privateNameInInExpression.types create mode 100644 tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f45c86e9ccb8d..89af87ac02a81 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1447,6 +1447,8 @@ namespace ts { return emitPostfixUnaryExpression(node); case SyntaxKind.BinaryExpression: return emitBinaryExpression(node); + case SyntaxKind.PrivateIdentifierInInExpression: + return emitPrivateIdentifierInInExpression(node); case SyntaxKind.ConditionalExpression: return emitConditionalExpression(node); case SyntaxKind.TemplateExpression: @@ -2552,6 +2554,7 @@ namespace ts { state.declarationListContainerEndStack[state.stackIndex] = declarationListContainerEnd; const emitComments = state.shouldEmitCommentsStack[state.stackIndex] = shouldEmitComments(node); const emitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex] = shouldEmitSourceMaps(node); + writeToken(SyntaxKind.OpenParenToken, node.pos, writePunctuation); // TODO(aclaymore): remove - for debugging beforeEmitWithContext(node, emitComments, emitSourceMaps); } else { @@ -2591,6 +2594,7 @@ namespace ts { const shouldEmitComments = state.shouldEmitCommentsStack[state.stackIndex]; const shouldEmitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex]; afterEmitWithContext(node, shouldEmitComments, shouldEmitSourceMaps, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd, savedPreserveSourceNewlines); + writeToken(SyntaxKind.CloseParenToken, node.pos, writePunctuation); // TODO(aclaymore): remove - for debugging state.stackIndex--; } } @@ -2605,6 +2609,17 @@ namespace ts { } } + function emitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { + // TODO(aclaymore) - emit better. Temp adding parenthesis for debugging + writeToken(SyntaxKind.OpenParenToken, node.pos, writePunctuation); + emit(node.name); + writeSpace(); + writeToken(SyntaxKind.InKeyword, node.pos, writePunctuation); + writeSpace(); + emit(node.expression); + writeToken(SyntaxKind.CloseParenToken, node.pos, writePunctuation); + } + function emitConditionalExpression(node: ConditionalExpression) { const linesBeforeQuestion = getLinesBetweenNodes(node, node.condition, node.questionToken); const linesAfterQuestion = getLinesBetweenNodes(node, node.questionToken, node.whenTrue); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index e443d4d0bdafb..ae65c847eae35 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -437,6 +437,8 @@ namespace ts { createMergeDeclarationMarker, createSyntheticReferenceExpression, updateSyntheticReferenceExpression, + createPrivateIdentifierInInExpression, + updatePrivateIdentifierInInExpression, cloneNode, // Lazily load factory methods for common operator factories and utilities @@ -3059,6 +3061,30 @@ namespace ts { : node; } + // @api + function createPrivateIdentifierInInExpression(name: PrivateIdentifier, expression: Expression) { + const node = createBaseExpression(SyntaxKind.PrivateIdentifierInInExpression); + node.name = name; + node.expression = expression; + node.transformFlags |= + propagateChildFlags(node.name) | + propagateChildFlags(node.expression) | + TransformFlags.ContainsESNext; + return node; + } + + // @api + function updatePrivateIdentifierInInExpression( + node: PrivateIdentifierInInExpression, + name: PrivateIdentifier, + expression: Expression + ): PrivateIdentifierInInExpression { + return node.name !== name + || node.expression !== expression + ? update(createPrivateIdentifierInInExpression(name, expression), node) + : node; + } + // // Misc // diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 64a88d32d099e..6001d909cd29f 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -444,6 +444,10 @@ namespace ts { return node.kind === SyntaxKind.CommaListExpression; } + export function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression { + return node.kind === SyntaxKind.PrivateIdentifierInInExpression; + } + // Misc export function isTemplateSpan(node: Node): node is TemplateSpan { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 92d6848d8a92b..0334a22666f93 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4430,9 +4430,27 @@ namespace ts { ); } + function parsePrivateIdentifierInInExpression(pos: number): Expression { + Debug.assert(token() === SyntaxKind.PrivateIdentifier, "parsePrivateIdentifierInInExpression should only have been called if we had a privateIdentifier"); + Debug.assert(inDisallowInContext() === false, "parsePrivateIdentifierInInExpression should only have been called if 'in' is allowed"); + const id = parsePrivateIdentifier(); + if (token() !== SyntaxKind.InKeyword) { + // TODO(aclaymore) use better error + return createMissingNode(SyntaxKind.InKeyword, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, tokenToString(SyntaxKind.InKeyword)); + } + nextToken(); + // TODO(aclaymore): LHS can be a binary expression of higher precedence + // const exp = parseUnaryExpressionOrHigher(); + const exp = parseBinaryExpressionOrHigher(OperatorPrecedence.Relational); + return finishNode(factory.createPrivateIdentifierInInExpression(id, exp), pos); + } + function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { const pos = getNodePos(); - const leftOperand = parseUnaryExpressionOrHigher(); + const tryPrivateIdentifierInIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext(); + const leftOperand = tryPrivateIdentifierInIn + ? parsePrivateIdentifierInInExpression(pos) + : parseUnaryExpressionOrHigher(); return parseBinaryExpressionRest(precedence, leftOperand, pos); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0deacbe8e8a6a..54d6b688cb5a4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -269,6 +269,7 @@ namespace ts { NonNullExpression, MetaProperty, SyntheticExpression, + PrivateIdentifierInInExpression, // Misc TemplateSpan, @@ -2305,6 +2306,12 @@ namespace ts { // see: https://tc39.github.io/ecma262/#prod-SuperProperty export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; + export interface PrivateIdentifierInInExpression extends Expression { + readonly kind: SyntaxKind.PrivateIdentifierInInExpression; + readonly name: PrivateIdentifier; + readonly expression: Expression; + } + export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -7088,6 +7095,8 @@ namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; + createPrivateIdentifierInInExpression(name: PrivateIdentifier, expression: Expression): PrivateIdentifierInInExpression; + updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, expression: Expression): PrivateIdentifierInInExpression; // // Misc diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 6bec2e5f07e86..673006c161c37 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1561,6 +1561,7 @@ namespace ts { case SyntaxKind.OmittedExpression: case SyntaxKind.CommaListExpression: case SyntaxKind.PartiallyEmittedExpression: + case SyntaxKind.PrivateIdentifierInInExpression: return true; default: return isUnaryExpressionKind(kind); diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt new file mode 100644 index 0000000000000..01086b447708f --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -0,0 +1,68 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,13): error TS1005: 'in' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,14): error TS2304: Cannot find name '#p1'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,14): error TS18016: Private identifiers are not allowed outside class bodies. + + +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (3 errors) ==== + // TODO(aclaymore) split up into seperate cases + + class Foo { + #p1 = 1; + m1(v: {}) { + #p1 in v; // Good + } + m2(v: any) { + #p1 in v.p1.p2; // Good + } + m3(v: unknown) { + #p1 in v; // Bad - RHS of in must be object type or any + } + m4(v: any) { + #p2 in v; // Bad - Invalid private id + } + m5(v: any) { + (#p1) in v; // Bad - private id is not an expression on it's own + +!!! error TS1005: 'in' expected. + } + m6(v: any) { + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed + ~~~ +!!! error TS2304: Cannot find name '#p1'. + ~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + } + m7(v: any) { + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + } + m8(v: any) { + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + } + m9(v: any) { + // '==' has lower precedence than 'in' + // '<' has same prededence than 'in' + // '<<' has higher prededence than 'in' + + v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + + v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + + v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + + v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + + #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + } + m10() { + class Bar { + m10(v: any) { + #p1 in v; // Good: access parent class + } + } + } + } + + function error(v: Foo) { + return #p1 in v; // Bad: outside of class + } + \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js new file mode 100644 index 0000000000000..44fbc3fa7f54f --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -0,0 +1,111 @@ +//// [privateNameInInExpression.ts] +// TODO(aclaymore) split up into seperate cases + +class Foo { + #p1 = 1; + m1(v: {}) { + #p1 in v; // Good + } + m2(v: any) { + #p1 in v.p1.p2; // Good + } + m3(v: unknown) { + #p1 in v; // Bad - RHS of in must be object type or any + } + m4(v: any) { + #p2 in v; // Bad - Invalid private id + } + m5(v: any) { + (#p1) in v; // Bad - private id is not an expression on it's own + } + m6(v: any) { + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed + } + m7(v: any) { + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + } + m8(v: any) { + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + } + m9(v: any) { + // '==' has lower precedence than 'in' + // '<' has same prededence than 'in' + // '<<' has higher prededence than 'in' + + v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + + v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + + v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + + v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + + #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + } + m10() { + class Bar { + m10(v: any) { + #p1 in v; // Good: access parent class + } + } + } +} + +function error(v: Foo) { + return #p1 in v; // Bad: outside of class +} + + +//// [privateNameInInExpression.js] +"use strict"; +// TODO(aclaymore) split up into seperate cases +class Foo { + constructor() { + (this.#p1 = 1); + } + #p1; + m1(v) { + (#p1 in v); // Good + } + m2(v) { + (#p1 in v.p1.p2); // Good + } + m3(v) { + (#p1 in v); // Bad - RHS of in must be object type or any + } + m4(v) { + (#p2 in v); // Bad - Invalid private id + } + m5(v) { + (() in v); // Bad - private id is not an expression on it's own + } + m6(v) { + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed + } + m7(v) { + for (let x in (#p1 in v)) { /* no-op */ } // Good - weird but valid + } + m8(v) { + for (let x in (#p1 in v)) { /* no-op */ } // Bad - rhs of in should be a object/any + } + m9(v) { + // '==' has lower precedence than 'in' + // '<' has same prededence than 'in' + // '<<' has higher prededence than 'in' + ((v == (#p1 in v)) == v); // Good precidence: ((v == (#p1 in v)) == v) + (v << (#p1 in (v << v))); // Good precidence: (v << (#p1 in (v << v))) + ((v << (#p1 in v)) == v); // Good precidence: ((v << (#p1 in v)) == v) + (v == ((#p1 in v) < v)); // Good precidence: (v == ((#p1 in v) < v)) + ((#p1 in v) && (#p1 in v)); // Good precidence: ((#p1 in v) && (#p1 in v)) + } + m10() { + class Bar { + m10(v) { + (#p1 in v); // Good: access parent class + } + } + } +} +function error(v) { + return (#p1 in v); // Bad: outside of class +} diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols new file mode 100644 index 0000000000000..2e091470d3e5f --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -0,0 +1,110 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === +// TODO(aclaymore) split up into seperate cases + +class Foo { +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) + + #p1 = 1; +>#p1 : Symbol(Foo.#p1, Decl(privateNameInInExpression.ts, 2, 11)) + + m1(v: {}) { +>m1 : Symbol(Foo.m1, Decl(privateNameInInExpression.ts, 3, 12)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 7)) + + #p1 in v; // Good + } + m2(v: any) { +>m2 : Symbol(Foo.m2, Decl(privateNameInInExpression.ts, 6, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 7, 7)) + + #p1 in v.p1.p2; // Good + } + m3(v: unknown) { +>m3 : Symbol(Foo.m3, Decl(privateNameInInExpression.ts, 9, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 10, 7)) + + #p1 in v; // Bad - RHS of in must be object type or any + } + m4(v: any) { +>m4 : Symbol(Foo.m4, Decl(privateNameInInExpression.ts, 12, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 13, 7)) + + #p2 in v; // Bad - Invalid private id + } + m5(v: any) { +>m5 : Symbol(Foo.m5, Decl(privateNameInInExpression.ts, 15, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 16, 7)) + + (#p1) in v; // Bad - private id is not an expression on it's own +>v : Symbol(v, Decl(privateNameInInExpression.ts, 16, 7)) + } + m6(v: any) { +>m6 : Symbol(Foo.m6, Decl(privateNameInInExpression.ts, 18, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 7)) + + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 7)) + } + m7(v: any) { +>m7 : Symbol(Foo.m7, Decl(privateNameInInExpression.ts, 21, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 22, 7)) + + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid +>x : Symbol(x, Decl(privateNameInInExpression.ts, 23, 16)) + } + m8(v: any) { +>m8 : Symbol(Foo.m8, Decl(privateNameInInExpression.ts, 24, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 25, 7)) + + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any +>x : Symbol(x, Decl(privateNameInInExpression.ts, 26, 16)) + } + m9(v: any) { +>m9 : Symbol(Foo.m9, Decl(privateNameInInExpression.ts, 27, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) + + // '==' has lower precedence than 'in' + // '<' has same prededence than 'in' + // '<<' has higher prededence than 'in' + + v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) + + v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) + + v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) + + v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) + + #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + } + m10() { +>m10 : Symbol(Foo.m10, Decl(privateNameInInExpression.ts, 42, 5)) + + class Bar { +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 43, 11)) + + m10(v: any) { +>m10 : Symbol(Bar.m10, Decl(privateNameInInExpression.ts, 44, 19)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 45, 16)) + + #p1 in v; // Good: access parent class + } + } + } +} + +function error(v: Foo) { +>error : Symbol(error, Decl(privateNameInInExpression.ts, 50, 1)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 52, 15)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) + + return #p1 in v; // Bad: outside of class +} + diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types new file mode 100644 index 0000000000000..a2e40e84d9227 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -0,0 +1,122 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === +// TODO(aclaymore) split up into seperate cases + +class Foo { +>Foo : Foo + + #p1 = 1; +>#p1 : number +>1 : 1 + + m1(v: {}) { +>m1 : (v: {}) => void +>v : {} + + #p1 in v; // Good + } + m2(v: any) { +>m2 : (v: any) => void +>v : any + + #p1 in v.p1.p2; // Good + } + m3(v: unknown) { +>m3 : (v: unknown) => void +>v : unknown + + #p1 in v; // Bad - RHS of in must be object type or any + } + m4(v: any) { +>m4 : (v: any) => void +>v : any + + #p2 in v; // Bad - Invalid private id + } + m5(v: any) { +>m5 : (v: any) => void +>v : any + + (#p1) in v; // Bad - private id is not an expression on it's own +>(#p1) in v : boolean +>(#p1) : any +>v : any + } + m6(v: any) { +>m6 : (v: any) => void +>v : any + + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed +>#p1 : any +>v : any + } + m7(v: any) { +>m7 : (v: any) => void +>v : any + + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid +>x : string +>#p1 in v as any : any + } + m8(v: any) { +>m8 : (v: any) => void +>v : any + + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any +>x : string + } + m9(v: any) { +>m9 : (v: any) => void +>v : any + + // '==' has lower precedence than 'in' + // '<' has same prededence than 'in' + // '<<' has higher prededence than 'in' + + v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) +>v == #p1 in v == v : boolean +>v == #p1 in v : boolean +>v : any +>v : any + + v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) +>v << #p1 in v << v : number +>v : any + + v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) +>v << #p1 in v == v : boolean +>v << #p1 in v : number +>v : any +>v : any + + v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) +>v == #p1 in v < v : boolean +>v : any +>#p1 in v < v : boolean +>v : any + + #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) +>#p1 in v && #p1 in v : any + } + m10() { +>m10 : () => void + + class Bar { +>Bar : Bar + + m10(v: any) { +>m10 : (v: any) => void +>v : any + + #p1 in v; // Good: access parent class + } + } + } +} + +function error(v: Foo) { +>error : (v: Foo) => any +>v : Foo + + return #p1 in v; // Bad: outside of class +} + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts new file mode 100644 index 0000000000000..507e94fa40609 --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -0,0 +1,58 @@ +// @strict: true +// @target: esnext + +// TODO(aclaymore) split up into seperate cases + +class Foo { + #p1 = 1; + m1(v: {}) { + #p1 in v; // Good + } + m2(v: any) { + #p1 in v.p1.p2; // Good + } + m3(v: unknown) { + #p1 in v; // Bad - RHS of in must be object type or any + } + m4(v: any) { + #p2 in v; // Bad - Invalid private id + } + m5(v: any) { + (#p1) in v; // Bad - private id is not an expression on it's own + } + m6(v: any) { + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed + } + m7(v: any) { + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + } + m8(v: any) { + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + } + m9(v: any) { + // '==' has lower precedence than 'in' + // '<' has same prededence than 'in' + // '<<' has higher prededence than 'in' + + v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + + v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + + v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + + v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + + #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + } + m10() { + class Bar { + m10(v: any) { + #p1 in v; // Good: access parent class + } + } + } +} + +function error(v: Foo) { + return #p1 in v; // Bad: outside of class +} From df7e97d2a090fc5e073bf826f51242848c3d68de Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 22 Feb 2021 19:29:21 +0000 Subject: [PATCH 02/29] basic type check and narrow Signed-off-by: Ashley Claymore --- src/compiler/binder.ts | 2 + src/compiler/checker.ts | 57 +++++++- src/compiler/parser.ts | 6 +- .../privateNameInInExpression.errors.txt | 93 +++++++----- .../reference/privateNameInInExpression.js | 114 ++++++++------- .../privateNameInInExpression.symbols | 134 ++++++++++-------- .../reference/privateNameInInExpression.types | 130 +++++++++++------ .../privateNames/privateNameInInExpression.ts | 62 ++++---- 8 files changed, 377 insertions(+), 221 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c6a2b14afdf97..a90af173a6b90 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -878,6 +878,8 @@ namespace ts { return (expr).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr).operand); case SyntaxKind.TypeOfExpression: return isNarrowingExpression((expr).expression); + case SyntaxKind.PrivateIdentifierInInExpression: + return isNarrowingExpression((expr).expression); } return false; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 193bb1d8c8099..7aee6c2d5f244 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23232,6 +23232,22 @@ namespace ts { return type; } + function narrowTypeByPrivateIdentifierInInExpression(type: Type, expr: PrivateIdentifierInInExpression, assumeTrue: boolean): Type { + const target = getReferenceCandidate(expr.expression); + if (!isMatchingReference(reference, target)) { + return type; + } + + const privateId = expr.name; + const klass = lookupClassForPrivateIdentifierDeclaration(privateId); + if (klass === undefined) { + return type; + } + + const classTypeForPrivateField = getTypeOfSymbolAtLocation(getSymbolOfNode(klass), klass); + return getNarrowedType(type, classTypeForPrivateField, 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. @@ -23645,6 +23661,8 @@ namespace ts { return narrowType(type, (expr).expression, assumeTrue); case SyntaxKind.BinaryExpression: return narrowTypeByBinaryExpression(type, expr, assumeTrue); + case SyntaxKind.PrivateIdentifierInInExpression: + return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue); case SyntaxKind.PrefixUnaryExpression: if ((expr).operator === SyntaxKind.ExclamationToken) { return narrowType(type, (expr).operand, !assumeTrue); @@ -26888,6 +26906,17 @@ namespace ts { } } + function lookupClassForPrivateIdentifierDeclaration(id: PrivateIdentifier): ClassLikeDeclaration | undefined { + for (let containingClass = getContainingClass(id); !!containingClass; containingClass = getContainingClass(containingClass)) { + const { symbol } = containingClass; + const name = getSymbolNameForPrivateIdentifier(symbol, id.escapedText); + const prop = (symbol.members && symbol.members.get(name)) || (symbol.exports && symbol.exports.get(name)); + if (prop) { + return containingClass; + } + } + } + function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined { return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName); } @@ -31056,6 +31085,11 @@ namespace ts { 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); } + checkInExpressionRHS(right, rightType); + return booleanType; + } + + function checkInExpressionRHS(right: Expression, rightType: Type) { const rightTypeConstraint = getConstraintOfType(rightType); if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) || rightTypeConstraint && ( @@ -31065,7 +31099,6 @@ namespace ts { ) { error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_not_be_a_primitive); } - return booleanType; } function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, rightIsThis?: boolean): Type { @@ -31302,6 +31335,26 @@ namespace ts { return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } + function checkPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, checkMode?: CheckMode) { + const privateId = node.name; + const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); + if (lexicallyScopedSymbol === undefined) { + // TODO(aclaymore): use better error message - we might be in a class but with no matching privateField + error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + return anyType; + } + + const exp = node.expression; + let rightType = checkExpression(exp, checkMode); + if (rightType === silentNeverType) { + return silentNeverType; + } + rightType = checkNonNullType(rightType, exp); + // TODO(aclaymore): Do RHS rules matching 'in' rules? (e.g. throw on null) + checkInExpressionRHS(exp, rightType); + return booleanType; + } + function createCheckBinaryExpression() { interface WorkArea { readonly checkMode: CheckMode | undefined; @@ -32483,6 +32536,8 @@ namespace ts { return checkPostfixUnaryExpression(node); case SyntaxKind.BinaryExpression: return checkBinaryExpression(node, checkMode); + case SyntaxKind.PrivateIdentifierInInExpression: + return checkPrivateIdentifierInInExpression(node, checkMode); case SyntaxKind.ConditionalExpression: return checkConditionalExpression(node, checkMode); case SyntaxKind.SpreadElement: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0334a22666f93..4b37609bd429c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -442,6 +442,9 @@ namespace ts { return visitNodes(cbNode, cbNodes, node.decorators); case SyntaxKind.CommaListExpression: return visitNodes(cbNode, cbNodes, (node).elements); + case SyntaxKind.PrivateIdentifierInInExpression: + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).expression); case SyntaxKind.JsxElement: return visitNode(cbNode, (node).openingElement) || @@ -4439,8 +4442,7 @@ namespace ts { return createMissingNode(SyntaxKind.InKeyword, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, tokenToString(SyntaxKind.InKeyword)); } nextToken(); - // TODO(aclaymore): LHS can be a binary expression of higher precedence - // const exp = parseUnaryExpressionOrHigher(); + // TODO(aclaymore) verify precedence is correct const exp = parseBinaryExpressionOrHigher(OperatorPrecedence.Relational); return finishNode(factory.createPrivateIdentifierInInExpression(id, exp), pos); } diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 01086b447708f..45716d06cfd7b 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,44 +1,55 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,13): error TS1005: 'in' expected. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,14): error TS2304: Cannot find name '#p1'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,14): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(14,26): error TS2571: Object is of type 'unknown'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(16,19): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,23): error TS1005: 'in' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS2304: Cannot find name '#p1'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,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(34,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(34,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(36,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(62,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (3 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (10 errors) ==== // TODO(aclaymore) split up into seperate cases class Foo { #p1 = 1; - m1(v: {}) { - #p1 in v; // Good - } - m2(v: any) { - #p1 in v.p1.p2; // Good - } - m3(v: unknown) { - #p1 in v; // Bad - RHS of in must be object type or any - } - m4(v: any) { - #p2 in v; // Bad - Invalid private id - } - m5(v: any) { - (#p1) in v; // Bad - private id is not an expression on it's own - + basics(v: any) { + const a = #p1 in v; // Good - a is boolean + + const b = #p1 in v.p1.p2; // Good - b is boolean + + const c = #p1 in (v as {}); // Good - c is boolean + + const d = #p1 in (v as Foo); // Good d is boolean (not true) + + const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + ~~~~~~~~~~~~~~ +!!! error TS2571: Object is of type 'unknown'. + + const f = #p2 in v; // Bad - Invalid privateID + ~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + + const g = (#p1) in v; // Bad - private id is not an expression on it's own + !!! error TS1005: 'in' expected. - } - m6(v: any) { + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed ~~~ !!! error TS2304: Cannot find name '#p1'. ~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. - } - m7(v: any) { - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid - } - m8(v: any) { + for (let x in #p1 in v) { /* no-op */ } // 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'. + + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + } - m9(v: any) { + precedence(v: any) { // '==' has lower precedence than 'in' // '<' has same prededence than 'in' // '<<' has higher prededence than 'in' @@ -46,23 +57,41 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + ~~~~~~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + ~~~~~~ +!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + ~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) } - m10() { - class Bar { - m10(v: any) { - #p1 in v; // Good: access parent class + flow(v: unknown) { + if (typeof v === 'object' && v !== null) { + if (#p1 in v) { + const y1 = v; // good y1 is typeof Foo + } else { + const y2 = v; // y2 is not typeof Foo + } + } + + class Nested { + m(v: any) { + if (#p1 in v) { + const y1 = v; // Good y1 if typeof Foo + } } } } } function error(v: Foo) { - return #p1 in v; // Bad: outside of class + return #p1 in v; // Bad - outside of class + ~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. } \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 44fbc3fa7f54f..107d706f86c2d 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -3,31 +3,29 @@ class Foo { #p1 = 1; - m1(v: {}) { - #p1 in v; // Good - } - m2(v: any) { - #p1 in v.p1.p2; // Good - } - m3(v: unknown) { - #p1 in v; // Bad - RHS of in must be object type or any - } - m4(v: any) { - #p2 in v; // Bad - Invalid private id - } - m5(v: any) { - (#p1) in v; // Bad - private id is not an expression on it's own - } - m6(v: any) { + basics(v: any) { + const a = #p1 in v; // Good - a is boolean + + const b = #p1 in v.p1.p2; // Good - b is boolean + + const c = #p1 in (v as {}); // Good - c is boolean + + const d = #p1 in (v as Foo); // Good d is boolean (not true) + + const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + + const f = #p2 in v; // Bad - Invalid privateID + + const g = (#p1) in v; // Bad - private id is not an expression on it's own + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed - } - m7(v: any) { - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid - } - m8(v: any) { + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + } - m9(v: any) { + precedence(v: any) { // '==' has lower precedence than 'in' // '<' has same prededence than 'in' // '<<' has higher prededence than 'in' @@ -42,17 +40,27 @@ class Foo { #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) } - m10() { - class Bar { - m10(v: any) { - #p1 in v; // Good: access parent class + flow(v: unknown) { + if (typeof v === 'object' && v !== null) { + if (#p1 in v) { + const y1 = v; // good y1 is typeof Foo + } else { + const y2 = v; // y2 is not typeof Foo + } + } + + class Nested { + m(v: any) { + if (#p1 in v) { + const y1 = v; // Good y1 if typeof Foo + } } } } } function error(v: Foo) { - return #p1 in v; // Bad: outside of class + return #p1 in v; // Bad - outside of class } @@ -64,31 +72,19 @@ class Foo { (this.#p1 = 1); } #p1; - m1(v) { - (#p1 in v); // Good - } - m2(v) { - (#p1 in v.p1.p2); // Good - } - m3(v) { - (#p1 in v); // Bad - RHS of in must be object type or any - } - m4(v) { - (#p2 in v); // Bad - Invalid private id - } - m5(v) { - (() in v); // Bad - private id is not an expression on it's own - } - m6(v) { + basics(v) { + const a = (#p1 in v); // Good - a is boolean + const b = (#p1 in v.p1.p2); // Good - b is boolean + const c = (#p1 in (v as {})); // Good - c is boolean + const d = (#p1 in (v as Foo)); // Good d is boolean (not true) + const e = (#p1 in (v as unknown)); // Bad - RHS of in must be object type or any + const f = (#p2 in v); // Bad - Invalid privateID + const g = (() in v); // Bad - private id is not an expression on it's own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed - } - m7(v) { - for (let x in (#p1 in v)) { /* no-op */ } // Good - weird but valid - } - m8(v) { for (let x in (#p1 in v)) { /* no-op */ } // Bad - rhs of in should be a object/any + for (let x in (#p1 in v)) { /* no-op */ } // Good - weird but valid } - m9(v) { + precedence(v) { // '==' has lower precedence than 'in' // '<' has same prededence than 'in' // '<<' has higher prededence than 'in' @@ -98,14 +94,24 @@ class Foo { (v == ((#p1 in v) < v)); // Good precidence: (v == ((#p1 in v) < v)) ((#p1 in v) && (#p1 in v)); // Good precidence: ((#p1 in v) && (#p1 in v)) } - m10() { - class Bar { - m10(v) { - (#p1 in v); // Good: access parent class + flow(v) { + if (((typeof v === 'object') && (v !== null))) { + if ((#p1 in v)) { + const y1 = v; // good y1 is typeof Foo + } + else { + const y2 = v; // y2 is not typeof Foo + } + } + class Nested { + m(v) { + if ((#p1 in v)) { + const y1 = v; // Good y1 if typeof Foo + } } } } } function error(v) { - return (#p1 in v); // Bad: outside of class + return (#p1 in v); // Bad - outside of class } diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 2e091470d3e5f..d2b9819d4b67e 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -7,104 +7,116 @@ class Foo { #p1 = 1; >#p1 : Symbol(Foo.#p1, Decl(privateNameInInExpression.ts, 2, 11)) - m1(v: {}) { ->m1 : Symbol(Foo.m1, Decl(privateNameInInExpression.ts, 3, 12)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 7)) + basics(v: any) { +>basics : Symbol(Foo.basics, Decl(privateNameInInExpression.ts, 3, 12)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) - #p1 in v; // Good - } - m2(v: any) { ->m2 : Symbol(Foo.m2, Decl(privateNameInInExpression.ts, 6, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 7, 7)) + const a = #p1 in v; // Good - a is boolean +>a : Symbol(a, Decl(privateNameInInExpression.ts, 5, 13)) - #p1 in v.p1.p2; // Good - } - m3(v: unknown) { ->m3 : Symbol(Foo.m3, Decl(privateNameInInExpression.ts, 9, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 10, 7)) + const b = #p1 in v.p1.p2; // Good - b is boolean +>b : Symbol(b, Decl(privateNameInInExpression.ts, 7, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) - #p1 in v; // Bad - RHS of in must be object type or any - } - m4(v: any) { ->m4 : Symbol(Foo.m4, Decl(privateNameInInExpression.ts, 12, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 13, 7)) + const c = #p1 in (v as {}); // Good - c is boolean +>c : Symbol(c, Decl(privateNameInInExpression.ts, 9, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) - #p2 in v; // Bad - Invalid private id - } - m5(v: any) { ->m5 : Symbol(Foo.m5, Decl(privateNameInInExpression.ts, 15, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 16, 7)) + const d = #p1 in (v as Foo); // Good d is boolean (not true) +>d : Symbol(d, Decl(privateNameInInExpression.ts, 11, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) - (#p1) in v; // Bad - private id is not an expression on it's own ->v : Symbol(v, Decl(privateNameInInExpression.ts, 16, 7)) - } - m6(v: any) { ->m6 : Symbol(Foo.m6, Decl(privateNameInInExpression.ts, 18, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 7)) + const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any +>e : Symbol(e, Decl(privateNameInInExpression.ts, 13, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + + const f = #p2 in v; // Bad - Invalid privateID +>f : Symbol(f, Decl(privateNameInInExpression.ts, 15, 13)) + + const g = (#p1) in v; // Bad - private id is not an expression on it's own +>g : Symbol(g, Decl(privateNameInInExpression.ts, 17, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed ->v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 7)) - } - m7(v: any) { ->m7 : Symbol(Foo.m7, Decl(privateNameInInExpression.ts, 21, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 22, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any +>x : Symbol(x, Decl(privateNameInInExpression.ts, 21, 16)) for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid >x : Symbol(x, Decl(privateNameInInExpression.ts, 23, 16)) - } - m8(v: any) { ->m8 : Symbol(Foo.m8, Decl(privateNameInInExpression.ts, 24, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 25, 7)) - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any ->x : Symbol(x, Decl(privateNameInInExpression.ts, 26, 16)) } - m9(v: any) { ->m9 : Symbol(Foo.m9, Decl(privateNameInInExpression.ts, 27, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) + precedence(v: any) { +>precedence : Symbol(Foo.precedence, Decl(privateNameInInExpression.ts, 25, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) // '==' has lower precedence than 'in' // '<' has same prededence than 'in' // '<<' has higher prededence than 'in' v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 7)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) } - m10() { ->m10 : Symbol(Foo.m10, Decl(privateNameInInExpression.ts, 42, 5)) + flow(v: unknown) { +>flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 40, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) + + if (typeof v === 'object' && v !== null) { +>v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) + + if (#p1 in v) { + const y1 = v; // good y1 is typeof Foo +>y1 : Symbol(y1, Decl(privateNameInInExpression.ts, 44, 21)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) + + } else { + const y2 = v; // y2 is not typeof Foo +>y2 : Symbol(y2, Decl(privateNameInInExpression.ts, 46, 21)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) + } + } - class Bar { ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 43, 11)) + class Nested { +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 48, 9)) - m10(v: any) { ->m10 : Symbol(Bar.m10, Decl(privateNameInInExpression.ts, 44, 19)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 45, 16)) + m(v: any) { +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 50, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 51, 14)) - #p1 in v; // Good: access parent class + if (#p1 in v) { + const y1 = v; // Good y1 if typeof Foo +>y1 : Symbol(y1, Decl(privateNameInInExpression.ts, 53, 24)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 51, 14)) + } } } } } function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpression.ts, 50, 1)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 52, 15)) +>error : Symbol(error, Decl(privateNameInInExpression.ts, 58, 1)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 60, 15)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) - return #p1 in v; // Bad: outside of class + return #p1 in v; // Bad - outside of class } diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index a2e40e84d9227..210ccbd54a10c 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -8,64 +8,66 @@ class Foo { >#p1 : number >1 : 1 - m1(v: {}) { ->m1 : (v: {}) => void ->v : {} + basics(v: any) { +>basics : (v: any) => void +>v : any - #p1 in v; // Good - } - m2(v: any) { ->m2 : (v: any) => void + const a = #p1 in v; // Good - a is boolean +>a : boolean >v : any - #p1 in v.p1.p2; // Good - } - m3(v: unknown) { ->m3 : (v: unknown) => void ->v : unknown + const b = #p1 in v.p1.p2; // Good - b is boolean +>b : boolean +>v.p1.p2 : any +>v.p1 : any +>v : any +>p1 : any +>p2 : any - #p1 in v; // Bad - RHS of in must be object type or any - } - m4(v: any) { ->m4 : (v: any) => void + const c = #p1 in (v as {}); // Good - c is boolean +>c : boolean +>(v as {}) : {} +>v as {} : {} >v : any - #p2 in v; // Bad - Invalid private id - } - m5(v: any) { ->m5 : (v: any) => void + const d = #p1 in (v as Foo); // Good d is boolean (not true) +>d : boolean +>(v as Foo) : Foo +>v as Foo : Foo +>v : any + + const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any +>e : boolean +>(v as unknown) : unknown +>v as unknown : unknown +>v : any + + const f = #p2 in v; // Bad - Invalid privateID +>f : any >v : any - (#p1) in v; // Bad - private id is not an expression on it's own + const g = (#p1) in v; // Bad - private id is not an expression on it's own +>g : boolean >(#p1) in v : boolean >(#p1) : any ->v : any - } - m6(v: any) { ->m6 : (v: any) => void >v : any for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed >#p1 : any >v : any - } - m7(v: any) { ->m7 : (v: any) => void + + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any +>x : string >v : any for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid >x : string >#p1 in v as any : any - } - m8(v: any) { ->m8 : (v: any) => void >v : any - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any ->x : string } - m9(v: any) { ->m9 : (v: any) => void + precedence(v: any) { +>precedence : (v: any) => void >v : any // '==' has lower precedence than 'in' @@ -76,38 +78,77 @@ class Foo { >v == #p1 in v == v : boolean >v == #p1 in v : boolean >v : any +>v : any >v : any v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) >v << #p1 in v << v : number +>v : any +>v << v : number +>v : any >v : any v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) >v << #p1 in v == v : boolean >v << #p1 in v : number >v : any +>v : any >v : any v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) >v == #p1 in v < v : boolean >v : any >#p1 in v < v : boolean +>v : any >v : any #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) ->#p1 in v && #p1 in v : any +>#p1 in v && #p1 in v : boolean +>v : any +>v : any } - m10() { ->m10 : () => void + flow(v: unknown) { +>flow : (v: unknown) => void +>v : unknown - class Bar { ->Bar : Bar + if (typeof v === 'object' && v !== null) { +>typeof v === 'object' && v !== null : boolean +>typeof v === 'object' : boolean +>typeof v : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>v : unknown +>'object' : "object" +>v !== null : boolean +>v : object | null +>null : null - m10(v: any) { ->m10 : (v: any) => void + if (#p1 in v) { >v : any - #p1 in v; // Good: access parent class + const y1 = v; // good y1 is typeof Foo +>y1 : typeof Foo +>v : typeof Foo + + } else { + const y2 = v; // y2 is not typeof Foo +>y2 : object +>v : object + } + } + + class Nested { +>Nested : Nested + + m(v: any) { +>m : (v: any) => void +>v : any + + if (#p1 in v) { +>v : any + + const y1 = v; // Good y1 if typeof Foo +>y1 : typeof Foo +>v : typeof Foo + } } } } @@ -117,6 +158,7 @@ function error(v: Foo) { >error : (v: Foo) => any >v : Foo - return #p1 in v; // Bad: outside of class + return #p1 in v; // Bad - outside of class +>v : any } diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 507e94fa40609..69303522f6dd8 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -5,31 +5,29 @@ class Foo { #p1 = 1; - m1(v: {}) { - #p1 in v; // Good - } - m2(v: any) { - #p1 in v.p1.p2; // Good - } - m3(v: unknown) { - #p1 in v; // Bad - RHS of in must be object type or any - } - m4(v: any) { - #p2 in v; // Bad - Invalid private id - } - m5(v: any) { - (#p1) in v; // Bad - private id is not an expression on it's own - } - m6(v: any) { + basics(v: any) { + const a = #p1 in v; // Good - a is boolean + + const b = #p1 in v.p1.p2; // Good - b is boolean + + const c = #p1 in (v as {}); // Good - c is boolean + + const d = #p1 in (v as Foo); // Good d is boolean (not true) + + const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + + const f = #p2 in v; // Bad - Invalid privateID + + const g = (#p1) in v; // Bad - private id is not an expression on it's own + for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed - } - m7(v: any) { - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid - } - m8(v: any) { + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + + for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + } - m9(v: any) { + precedence(v: any) { // '==' has lower precedence than 'in' // '<' has same prededence than 'in' // '<<' has higher prededence than 'in' @@ -44,15 +42,25 @@ class Foo { #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) } - m10() { - class Bar { - m10(v: any) { - #p1 in v; // Good: access parent class + flow(v: unknown) { + if (typeof v === 'object' && v !== null) { + if (#p1 in v) { + const y1 = v; // good y1 is typeof Foo + } else { + const y2 = v; // y2 is not typeof Foo + } + } + + class Nested { + m(v: any) { + if (#p1 in v) { + const y1 = v; // Good y1 if typeof Foo + } } } } } function error(v: Foo) { - return #p1 in v; // Bad: outside of class + return #p1 in v; // Bad - outside of class } From 219899f212ceb38770232bc97177a9eaba33279e Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 23 Feb 2021 12:26:31 +0000 Subject: [PATCH 03/29] narrow to instanceOf klass not typeof klass Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 8 ++++++-- tests/baselines/reference/privateNameInInExpression.types | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7aee6c2d5f244..17613425ed03d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23244,8 +23244,12 @@ namespace ts { return type; } - const classTypeForPrivateField = getTypeOfSymbolAtLocation(getSymbolOfNode(klass), klass); - return getNarrowedType(type, classTypeForPrivateField, assumeTrue, isTypeDerivedFrom); + const classType = getTypeOfSymbolAtLocation(getSymbolOfNode(klass), klass); + const ctorSigs = getSignaturesOfType(classType, SignatureKind.Construct); + // TODO(aclaymore): verify assertion is valid + Debug.assert(ctorSigs.length > 0, "narrowTypeByPrivateIdentifierInInExpression should always find the class signature"); + const instanceType = getReturnTypeOfSignature(ctorSigs[0]); + return getNarrowedType(type, instanceType, assumeTrue, isTypeSubtypeOf); } function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 210ccbd54a10c..5d6af5c4e7c85 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -125,8 +125,8 @@ class Foo { >v : any const y1 = v; // good y1 is typeof Foo ->y1 : typeof Foo ->v : typeof Foo +>y1 : Foo +>v : Foo } else { const y2 = v; // y2 is not typeof Foo @@ -146,8 +146,8 @@ class Foo { >v : any const y1 = v; // Good y1 if typeof Foo ->y1 : typeof Foo ->v : typeof Foo +>y1 : Foo +>v : Foo } } } From fa3a4c786779df9d5aa938b47ff71c5b9e1f6cba Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 24 Feb 2021 18:56:16 +0000 Subject: [PATCH 04/29] tweaking tests Signed-off-by: Ashley Claymore --- .../privateNameInInExpression.errors.txt | 68 +++++++--- .../reference/privateNameInInExpression.js | 124 ++++++++++++++---- .../privateNameInInExpression.symbols | 109 +++++++++++---- .../reference/privateNameInInExpression.types | 117 ++++++++++++----- .../privateNames/privateNameInInExpression.ts | 61 +++++++-- 5 files changed, 362 insertions(+), 117 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 45716d06cfd7b..4c141943f8759 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -7,11 +7,12 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(34,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(34,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(36,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(62,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(46,24): error TS2531: Object is possibly 'null'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(93,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (10 errors) ==== - // TODO(aclaymore) split up into seperate cases +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (11 errors) ==== + // TODO(aclaymore) split up into separate cases class Foo { #p1 = 1; @@ -51,47 +52,82 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t } precedence(v: any) { // '==' has lower precedence than 'in' - // '<' has same prededence than 'in' - // '<<' has higher prededence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) - v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) ~~~~~~~~~~~~~ !!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. - v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) ~~~~~~~~ !!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. - v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) - #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(v: unknown) { - if (typeof v === 'object' && v !== null) { - if (#p1 in v) { - const y1 = v; // good y1 is typeof Foo + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { + + if (typeof u === 'object') { + + if (#p1 in u) { + ~ +!!! error TS2531: Object is possibly 'null'. + u; // good u is Foo } else { - const y2 = v; // y2 is not typeof Foo + u; // good u is object | null } + + if (u !== null) { + if (#p1 in u) { + u; // good u is Foo + } else { + u; // good u is object + } + } + } + + if (#p1 in fb) { + fb; // good fb is Foo + } else { + fb; // good fb is Bar + } + + if (#p1 in fs) { + fs; // good fb is Foo (or FooSub?) + } else { + fs; // good fs is never + } + + if (#p1 in b) { + b; // good b is 'Bar & Foo' + } else { + b; // good b is Bar } class Nested { m(v: any) { if (#p1 in v) { - const y1 = v; // Good y1 if typeof Foo + v; // good v is Foo } } } } } + class FooSub extends Foo { } + class Bar { notFoo = true } + function error(v: Foo) { return #p1 in v; // Bad - outside of class ~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } + + export { } \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 107d706f86c2d..2aba44665874d 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -1,5 +1,5 @@ //// [privateNameInInExpression.ts] -// TODO(aclaymore) split up into seperate cases +// TODO(aclaymore) split up into separate cases class Foo { #p1 = 1; @@ -27,46 +27,78 @@ class Foo { } precedence(v: any) { // '==' has lower precedence than 'in' - // '<' has same prededence than 'in' - // '<<' has higher prededence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) - v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) - v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) - v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) - #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(v: unknown) { - if (typeof v === 'object' && v !== null) { - if (#p1 in v) { - const y1 = v; // good y1 is typeof Foo + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { + + if (typeof u === 'object') { + + if (#p1 in u) { + u; // good u is Foo } else { - const y2 = v; // y2 is not typeof Foo + u; // good u is object | null } + + if (u !== null) { + if (#p1 in u) { + u; // good u is Foo + } else { + u; // good u is object + } + } + } + + if (#p1 in fb) { + fb; // good fb is Foo + } else { + fb; // good fb is Bar + } + + if (#p1 in fs) { + fs; // good fb is Foo (or FooSub?) + } else { + fs; // good fs is never + } + + if (#p1 in b) { + b; // good b is 'Bar & Foo' + } else { + b; // good b is Bar } class Nested { m(v: any) { if (#p1 in v) { - const y1 = v; // Good y1 if typeof Foo + v; // good v is Foo } } } } } +class FooSub extends Foo { } +class Bar { notFoo = true } + function error(v: Foo) { return #p1 in v; // Bad - outside of class } + +export { } //// [privateNameInInExpression.js] -"use strict"; -// TODO(aclaymore) split up into seperate cases +// TODO(aclaymore) split up into separate cases class Foo { constructor() { (this.#p1 = 1); @@ -86,32 +118,66 @@ class Foo { } precedence(v) { // '==' has lower precedence than 'in' - // '<' has same prededence than 'in' - // '<<' has higher prededence than 'in' - ((v == (#p1 in v)) == v); // Good precidence: ((v == (#p1 in v)) == v) - (v << (#p1 in (v << v))); // Good precidence: (v << (#p1 in (v << v))) - ((v << (#p1 in v)) == v); // Good precidence: ((v << (#p1 in v)) == v) - (v == ((#p1 in v) < v)); // Good precidence: (v == ((#p1 in v) < v)) - ((#p1 in v) && (#p1 in v)); // Good precidence: ((#p1 in v) && (#p1 in v)) + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + ((v == (#p1 in v)) == v); // Good precedence: ((v == (#p1 in v)) == v) + (v << (#p1 in (v << v))); // Good precedence: (v << (#p1 in (v << v))) + ((v << (#p1 in v)) == v); // Good precedence: ((v << (#p1 in v)) == v) + (v == ((#p1 in v) < v)); // Good precedence: (v == ((#p1 in v) < v)) + ((#p1 in v) && (#p1 in v)); // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(v) { - if (((typeof v === 'object') && (v !== null))) { - if ((#p1 in v)) { - const y1 = v; // good y1 is typeof Foo + flow(u, fb, fs, b) { + if ((typeof u === 'object')) { + if ((#p1 in u)) { + u; // good u is Foo } else { - const y2 = v; // y2 is not typeof Foo + u; // good u is object | null } + if ((u !== null)) { + if ((#p1 in u)) { + u; // good u is Foo + } + else { + u; // good u is object + } + } + } + if ((#p1 in fb)) { + fb; // good fb is Foo + } + else { + fb; // good fb is Bar + } + if ((#p1 in fs)) { + fs; // good fb is Foo (or FooSub?) + } + else { + fs; // good fs is never + } + if ((#p1 in b)) { + b; // good b is 'Bar & Foo' + } + else { + b; // good b is Bar } class Nested { m(v) { if ((#p1 in v)) { - const y1 = v; // Good y1 if typeof Foo + v; // good v is Foo } } } } } +class FooSub extends Foo { +} +class Bar { + constructor() { + (this.notFoo = true); + } +} function error(v) { return (#p1 in v); // Bad - outside of class } +export {}; diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index d2b9819d4b67e..d5a705d4ef57f 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -1,5 +1,5 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === -// TODO(aclaymore) split up into seperate cases +// TODO(aclaymore) split up into separate cases class Foo { >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) @@ -53,70 +53,123 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) // '==' has lower precedence than 'in' - // '<' has same prededence than 'in' - // '<<' has higher prededence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(v: unknown) { + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { >flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 40, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 88, 28)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 86, 1)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 88, 28)) - if (typeof v === 'object' && v !== null) { ->v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) + if (typeof u === 'object') { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) - if (#p1 in v) { - const y1 = v; // good y1 is typeof Foo ->y1 : Symbol(y1, Decl(privateNameInInExpression.ts, 44, 21)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) + if (#p1 in u) { + u; // good u is Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) } else { - const y2 = v; // y2 is not typeof Foo ->y2 : Symbol(y2, Decl(privateNameInInExpression.ts, 46, 21)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 41, 9)) + u; // good u is object | null +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) + } + + if (u !== null) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) + + if (#p1 in u) { + u; // good u is Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) + + } else { + u; // good u is object +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) + } } } + if (#p1 in fb) { + fb; // good fb is Foo +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) + + } else { + fb; // good fb is Bar +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) + } + + if (#p1 in fs) { + fs; // good fb is Foo (or FooSub?) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) + + } else { + fs; // good fs is never +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) + } + + if (#p1 in b) { + b; // good b is 'Bar & Foo' +>b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) + + } else { + b; // good b is Bar +>b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) + } + class Nested { ->Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 48, 9)) +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 76, 9)) m(v: any) { ->m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 50, 22)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 51, 14)) +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 78, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) if (#p1 in v) { - const y1 = v; // Good y1 if typeof Foo ->y1 : Symbol(y1, Decl(privateNameInInExpression.ts, 53, 24)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 51, 14)) + v; // good v is Foo +>v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) } } } } } +class FooSub extends Foo { } +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 86, 1)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) + +class Bar { notFoo = true } +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 88, 28)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 89, 11)) + function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpression.ts, 58, 1)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 60, 15)) +>error : Symbol(error, Decl(privateNameInInExpression.ts, 89, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 91, 15)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) return #p1 in v; // Bad - outside of class } +export { } + diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 5d6af5c4e7c85..3c95583467fc6 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -1,5 +1,5 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === -// TODO(aclaymore) split up into seperate cases +// TODO(aclaymore) split up into separate cases class Foo { >Foo : Foo @@ -71,68 +71,115 @@ class Foo { >v : any // '==' has lower precedence than 'in' - // '<' has same prededence than 'in' - // '<<' has higher prededence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) >v == #p1 in v == v : boolean >v == #p1 in v : boolean >v : any >v : any >v : any - v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) >v << #p1 in v << v : number >v : any >v << v : number >v : any >v : any - v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) >v << #p1 in v == v : boolean >v << #p1 in v : number >v : any >v : any >v : any - v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) >v == #p1 in v < v : boolean >v : any >#p1 in v < v : boolean >v : any >v : any - #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) >#p1 in v && #p1 in v : boolean >v : any >v : any } - flow(v: unknown) { ->flow : (v: unknown) => void ->v : unknown - - if (typeof v === 'object' && v !== null) { ->typeof v === 'object' && v !== null : boolean ->typeof v === 'object' : boolean ->typeof v : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->v : unknown + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { +>flow : (u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) => void +>u : unknown +>fb : Foo | Bar +>fs : FooSub +>b : Bar + + if (typeof u === 'object') { +>typeof u === 'object' : boolean +>typeof u : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>u : unknown >'object' : "object" ->v !== null : boolean ->v : object | null ->null : null - if (#p1 in v) { ->v : any + if (#p1 in u) { +>u : any - const y1 = v; // good y1 is typeof Foo ->y1 : Foo ->v : Foo + u; // good u is Foo +>u : Foo } else { - const y2 = v; // y2 is not typeof Foo ->y2 : object ->v : object + u; // good u is object | null +>u : object | null } + + if (u !== null) { +>u !== null : boolean +>u : object | null +>null : null + + if (#p1 in u) { +>u : any + + u; // good u is Foo +>u : Foo + + } else { + u; // good u is object +>u : object + } + } + } + + if (#p1 in fb) { +>fb : any + + fb; // good fb is Foo +>fb : Foo + + } else { + fb; // good fb is Bar +>fb : Bar + } + + if (#p1 in fs) { +>fs : any + + fs; // good fb is Foo (or FooSub?) +>fs : Foo + + } else { + fs; // good fs is never +>fs : never + } + + if (#p1 in b) { +>b : any + + b; // good b is 'Bar & Foo' +>b : Bar & Foo + + } else { + b; // good b is Bar +>b : Bar } class Nested { @@ -145,8 +192,7 @@ class Foo { if (#p1 in v) { >v : any - const y1 = v; // Good y1 if typeof Foo ->y1 : Foo + v; // good v is Foo >v : Foo } } @@ -154,6 +200,15 @@ class Foo { } } +class FooSub extends Foo { } +>FooSub : FooSub +>Foo : Foo + +class Bar { notFoo = true } +>Bar : Bar +>notFoo : boolean +>true : true + function error(v: Foo) { >error : (v: Foo) => any >v : Foo @@ -162,3 +217,5 @@ function error(v: Foo) { >v : any } +export { } + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 69303522f6dd8..584c81b7564c8 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -1,7 +1,7 @@ // @strict: true // @target: esnext -// TODO(aclaymore) split up into seperate cases +// TODO(aclaymore) split up into separate cases class Foo { #p1 = 1; @@ -29,38 +29,71 @@ class Foo { } precedence(v: any) { // '==' has lower precedence than 'in' - // '<' has same prededence than 'in' - // '<<' has higher prededence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precidence: ((v == (#p1 in v)) == v) + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) - v << #p1 in v << v; // Good precidence: (v << (#p1 in (v << v))) + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) - v << #p1 in v == v; // Good precidence: ((v << (#p1 in v)) == v) + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) - v == #p1 in v < v; // Good precidence: (v == ((#p1 in v) < v)) + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) - #p1 in v && #p1 in v; // Good precidence: ((#p1 in v) && (#p1 in v)) + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(v: unknown) { - if (typeof v === 'object' && v !== null) { - if (#p1 in v) { - const y1 = v; // good y1 is typeof Foo + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { + + if (typeof u === 'object') { + + if (#p1 in u) { + u; // good u is Foo } else { - const y2 = v; // y2 is not typeof Foo + u; // good u is object | null } + + if (u !== null) { + if (#p1 in u) { + u; // good u is Foo + } else { + u; // good u is object + } + } + } + + if (#p1 in fb) { + fb; // good fb is Foo + } else { + fb; // good fb is Bar + } + + if (#p1 in fs) { + fs; // good fb is Foo (or FooSub?) + } else { + fs; // good fs is never + } + + if (#p1 in b) { + b; // good b is 'Bar & Foo' + } else { + b; // good b is Bar } class Nested { m(v: any) { if (#p1 in v) { - const y1 = v; // Good y1 if typeof Foo + v; // good v is Foo } } } } } +class FooSub extends Foo { } +class Bar { notFoo = true } + function error(v: Foo) { return #p1 in v; // Bad - outside of class } + +export { } From 7df6a992f707113f65f108e3623ff7f700993971 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 25 Feb 2021 16:03:25 +0000 Subject: [PATCH 05/29] fix emit with type syntax on rhs Signed-off-by: Ashley Claymore --- src/compiler/emitter.ts | 2 +- src/compiler/utilities.ts | 4 +++ src/compiler/visitorPublic.ts | 5 +++ .../reference/privateNameInInExpression.js | 6 ++-- .../privateNameInInExpression.symbols | 22 ++++++++++++ .../reference/privateNameInInExpression.types | 35 +++++++++++++++---- 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 89af87ac02a81..558e5e97e6bc0 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2612,7 +2612,7 @@ namespace ts { function emitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { // TODO(aclaymore) - emit better. Temp adding parenthesis for debugging writeToken(SyntaxKind.OpenParenToken, node.pos, writePunctuation); - emit(node.name); + emitPrivateIdentifier(node.name); writeSpace(); writeToken(SyntaxKind.InKeyword, node.pos, writePunctuation); writeSpace(); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0b7e5f2003eb9..8ee4a4d08e548 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1884,6 +1884,7 @@ namespace ts { case SyntaxKind.YieldExpression: case SyntaxKind.AwaitExpression: case SyntaxKind.MetaProperty: + case SyntaxKind.PrivateIdentifierInInExpression: return true; case SyntaxKind.QualifiedName: while (node.parent.kind === SyntaxKind.QualifiedName) { @@ -3579,6 +3580,9 @@ namespace ts { return getBinaryOperatorPrecedence(operatorKind); } + case SyntaxKind.PrivateIdentifierInInExpression: + return OperatorPrecedence.Relational; + // TODO: Should prefix `++` and `--` be moved to the `Update` precedence? case SyntaxKind.TypeAssertionExpression: case SyntaxKind.NonNullExpression: diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 960cd16f8479b..5ec6dcd7cc202 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -793,6 +793,11 @@ namespace ts { nodeVisitor(node.operatorToken, tokenVisitor, isBinaryOperatorToken), nodeVisitor(node.right, visitor, isExpression)); + case SyntaxKind.PrivateIdentifierInInExpression: + return factory.updatePrivateIdentifierInInExpression(node, + nodeVisitor((node).name, visitor, isPrivateIdentifier), + nodeVisitor((node).expression, visitor, isExpression)); + case SyntaxKind.ConditionalExpression: Debug.type(node); return factory.updateConditionalExpression(node, diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 2aba44665874d..b38da036ccbf7 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -107,9 +107,9 @@ class Foo { basics(v) { const a = (#p1 in v); // Good - a is boolean const b = (#p1 in v.p1.p2); // Good - b is boolean - const c = (#p1 in (v as {})); // Good - c is boolean - const d = (#p1 in (v as Foo)); // Good d is boolean (not true) - const e = (#p1 in (v as unknown)); // Bad - RHS of in must be object type or any + const c = (#p1 in v); // Good - c is boolean + const d = (#p1 in v); // Good d is boolean (not true) + const e = (#p1 in v); // Bad - RHS of in must be object type or any const f = (#p2 in v); // Bad - Invalid privateID const g = (() in v); // Bad - private id is not an expression on it's own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index d5a705d4ef57f..04c47e8c45946 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -13,6 +13,7 @@ class Foo { const a = #p1 in v; // Good - a is boolean >a : Symbol(a, Decl(privateNameInInExpression.ts, 5, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) const b = #p1 in v.p1.p2; // Good - b is boolean >b : Symbol(b, Decl(privateNameInInExpression.ts, 7, 13)) @@ -33,6 +34,7 @@ class Foo { const f = #p2 in v; // Bad - Invalid privateID >f : Symbol(f, Decl(privateNameInInExpression.ts, 15, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) const g = (#p1) in v; // Bad - private id is not an expression on it's own >g : Symbol(g, Decl(privateNameInInExpression.ts, 17, 13)) @@ -43,9 +45,11 @@ class Foo { for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any >x : Symbol(x, Decl(privateNameInInExpression.ts, 21, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid >x : Symbol(x, Decl(privateNameInInExpression.ts, 23, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) } precedence(v: any) { @@ -58,6 +62,7 @@ class Foo { v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) @@ -67,13 +72,17 @@ class Foo { v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) } flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { >flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 40, 5)) @@ -90,6 +99,8 @@ class Foo { >u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) if (#p1 in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) + u; // good u is Foo >u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) @@ -102,6 +113,8 @@ class Foo { >u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) if (#p1 in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) + u; // good u is Foo >u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) @@ -113,6 +126,8 @@ class Foo { } if (#p1 in fb) { +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) + fb; // good fb is Foo >fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) @@ -122,6 +137,8 @@ class Foo { } if (#p1 in fs) { +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) + fs; // good fb is Foo (or FooSub?) >fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) @@ -131,6 +148,8 @@ class Foo { } if (#p1 in b) { +>b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) + b; // good b is 'Bar & Foo' >b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) @@ -147,6 +166,8 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) if (#p1 in v) { +>v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) + v; // good v is Foo >v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) } @@ -169,6 +190,7 @@ function error(v: Foo) { >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) return #p1 in v; // Bad - outside of class +>v : Symbol(v, Decl(privateNameInInExpression.ts, 91, 15)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 3c95583467fc6..59e61227db9d9 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -14,10 +14,12 @@ class Foo { const a = #p1 in v; // Good - a is boolean >a : boolean +>#p1 in v : boolean >v : any const b = #p1 in v.p1.p2; // Good - b is boolean >b : boolean +>#p1 in v.p1.p2 : boolean >v.p1.p2 : any >v.p1 : any >v : any @@ -26,24 +28,28 @@ class Foo { const c = #p1 in (v as {}); // Good - c is boolean >c : boolean +>#p1 in (v as {}) : boolean >(v as {}) : {} >v as {} : {} >v : any const d = #p1 in (v as Foo); // Good d is boolean (not true) >d : boolean +>#p1 in (v as Foo) : boolean >(v as Foo) : Foo >v as Foo : Foo >v : any const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any >e : boolean +>#p1 in (v as unknown) : boolean >(v as unknown) : unknown >v as unknown : unknown >v : any const f = #p2 in v; // Bad - Invalid privateID >f : any +>#p2 in v : any >v : any const g = (#p1) in v; // Bad - private id is not an expression on it's own @@ -58,11 +64,13 @@ class Foo { for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any >x : string +>#p1 in v : boolean >v : any for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid >x : string >#p1 in v as any : any +>#p1 in v : boolean >v : any } @@ -78,12 +86,14 @@ class Foo { >v == #p1 in v == v : boolean >v == #p1 in v : boolean >v : any +>#p1 in v : boolean >v : any >v : any v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) >v << #p1 in v << v : number >v : any +>#p1 in v << v : boolean >v << v : number >v : any >v : any @@ -92,6 +102,7 @@ class Foo { >v << #p1 in v == v : boolean >v << #p1 in v : number >v : any +>#p1 in v : boolean >v : any >v : any @@ -99,13 +110,16 @@ class Foo { >v == #p1 in v < v : boolean >v : any >#p1 in v < v : boolean +>#p1 in v : boolean >v : any >v : any #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) >#p1 in v && #p1 in v : boolean +>#p1 in v : boolean >v : any ->v : any +>#p1 in v : boolean +>v : Foo } flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { >flow : (u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) => void @@ -121,7 +135,8 @@ class Foo { >'object' : "object" if (#p1 in u) { ->u : any +>#p1 in u : boolean +>u : object | null u; // good u is Foo >u : Foo @@ -137,7 +152,8 @@ class Foo { >null : null if (#p1 in u) { ->u : any +>#p1 in u : boolean +>u : object u; // good u is Foo >u : Foo @@ -150,7 +166,8 @@ class Foo { } if (#p1 in fb) { ->fb : any +>#p1 in fb : boolean +>fb : Foo | Bar fb; // good fb is Foo >fb : Foo @@ -161,7 +178,8 @@ class Foo { } if (#p1 in fs) { ->fs : any +>#p1 in fs : boolean +>fs : FooSub fs; // good fb is Foo (or FooSub?) >fs : Foo @@ -172,7 +190,8 @@ class Foo { } if (#p1 in b) { ->b : any +>#p1 in b : boolean +>b : Bar b; // good b is 'Bar & Foo' >b : Bar & Foo @@ -190,6 +209,7 @@ class Foo { >v : any if (#p1 in v) { +>#p1 in v : boolean >v : any v; // good v is Foo @@ -214,7 +234,8 @@ function error(v: Foo) { >v : Foo return #p1 in v; // Bad - outside of class ->v : any +>#p1 in v : any +>v : Foo } export { } From df5a86feb1597e5cf2caeb4ee1dd800643277778 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 25 Feb 2021 16:13:59 +0000 Subject: [PATCH 06/29] add test case for subclass union narrow Signed-off-by: Ashley Claymore --- .../privateNameInInExpression.errors.txt | 10 ++++- .../reference/privateNameInInExpression.js | 16 ++++++- .../privateNameInInExpression.symbols | 44 ++++++++++++------- .../reference/privateNameInInExpression.types | 17 ++++++- .../privateNames/privateNameInInExpression.ts | 8 +++- 5 files changed, 73 insertions(+), 22 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 4c141943f8759..870bbee56da6a 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -8,7 +8,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(34,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(36,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(46,24): error TS2531: Object is possibly 'null'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(93,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(99,12): error TS18016: Private identifiers are not allowed outside class bodies. ==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (11 errors) ==== @@ -71,7 +71,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { if (typeof u === 'object') { @@ -110,6 +110,12 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t b; // good b is Bar } + if (#p1 in fsb) { + fsb; // good fsb is FooSub + } else { + fsb; // good fsb is Bar + } + class Nested { m(v: any) { if (#p1 in v) { diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index b38da036ccbf7..71535bcc89f37 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -40,7 +40,7 @@ class Foo { #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { if (typeof u === 'object') { @@ -77,6 +77,12 @@ class Foo { b; // good b is Bar } + if (#p1 in fsb) { + fsb; // good fsb is FooSub + } else { + fsb; // good fsb is Bar + } + class Nested { m(v: any) { if (#p1 in v) { @@ -126,7 +132,7 @@ class Foo { (v == ((#p1 in v) < v)); // Good precedence: (v == ((#p1 in v) < v)) ((#p1 in v) && (#p1 in v)); // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(u, fb, fs, b) { + flow(u, fb, fs, b, fsb) { if ((typeof u === 'object')) { if ((#p1 in u)) { u; // good u is Foo @@ -161,6 +167,12 @@ class Foo { else { b; // good b is Bar } + if ((#p1 in fsb)) { + fsb; // good fsb is FooSub + } + else { + fsb; // good fsb is Bar + } class Nested { m(v) { if ((#p1 in v)) { diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 04c47e8c45946..0615eb7488bc5 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -84,16 +84,19 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { >flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 40, 5)) >u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) >fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 88, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) >fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 86, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 92, 1)) >b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 88, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 92, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) if (typeof u === 'object') { >u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) @@ -158,18 +161,29 @@ class Foo { >b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) } + if (#p1 in fsb) { +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) + + fsb; // good fsb is FooSub +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) + + } else { + fsb; // good fsb is Bar +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) + } + class Nested { ->Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 76, 9)) +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 82, 9)) m(v: any) { ->m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 78, 22)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 84, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 85, 14)) if (#p1 in v) { ->v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 85, 14)) v; // good v is Foo ->v : Symbol(v, Decl(privateNameInInExpression.ts, 79, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 85, 14)) } } } @@ -177,20 +191,20 @@ class Foo { } class FooSub extends Foo { } ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 86, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 92, 1)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) class Bar { notFoo = true } ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 88, 28)) ->notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 89, 11)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 95, 11)) function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpression.ts, 89, 27)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 91, 15)) +>error : Symbol(error, Decl(privateNameInInExpression.ts, 95, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 97, 15)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) return #p1 in v; // Bad - outside of class ->v : Symbol(v, Decl(privateNameInInExpression.ts, 91, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 97, 15)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 59e61227db9d9..7553703c86b86 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -121,12 +121,13 @@ class Foo { >#p1 in v : boolean >v : Foo } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { ->flow : (u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) => void + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { +>flow : (u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) => void >u : unknown >fb : Foo | Bar >fs : FooSub >b : Bar +>fsb : Bar | FooSub if (typeof u === 'object') { >typeof u === 'object' : boolean @@ -201,6 +202,18 @@ class Foo { >b : Bar } + if (#p1 in fsb) { +>#p1 in fsb : boolean +>fsb : Bar | FooSub + + fsb; // good fsb is FooSub +>fsb : FooSub + + } else { + fsb; // good fsb is Bar +>fsb : Bar + } + class Nested { >Nested : Nested diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 584c81b7564c8..ce90056a787cd 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -42,7 +42,7 @@ class Foo { #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar) { + flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { if (typeof u === 'object') { @@ -79,6 +79,12 @@ class Foo { b; // good b is Bar } + if (#p1 in fsb) { + fsb; // good fsb is FooSub + } else { + fsb; // good fsb is Bar + } + class Nested { m(v: any) { if (#p1 in v) { From 3560223efd4db78c60cfe7e6d167f4ccb52830bc Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 25 Feb 2021 18:13:09 +0000 Subject: [PATCH 07/29] es2020 emit Signed-off-by: Ashley Claymore --- src/compiler/transformers/classFields.ts | 32 ++++++++++++ src/compiler/visitorPublic.ts | 2 +- ...vateNameInInExpressionTransform.errors.txt | 29 +++++++++++ .../privateNameInInExpressionTransform.js | 51 +++++++++++++++++++ ...privateNameInInExpressionTransform.symbols | 45 ++++++++++++++++ .../privateNameInInExpressionTransform.types | 49 ++++++++++++++++++ .../privateNameInInExpressionTransform.ts | 24 +++++++++ 7 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.errors.txt create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.js create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.symbols create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.types create mode 100644 tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 869fb42f18f03..0d78aadd0978d 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -175,6 +175,8 @@ namespace ts { return visitForStatement(node as ForStatement); case SyntaxKind.TaggedTemplateExpression: return visitTaggedTemplateExpression(node as TaggedTemplateExpression); + case SyntaxKind.PrivateIdentifierInInExpression: + return visitPrivateIdentifierInInExpression(node as PrivateIdentifierInInExpression); } return visitEachChild(node, visitor, context); } @@ -200,6 +202,36 @@ namespace ts { return setOriginalNode(factory.createIdentifier(""), node); } + /** + * Visits `#id in expr` + */ + function visitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { + if (!shouldTransformPrivateFields) { + return node; + } + const info = accessPrivateIdentifier(node.name); + if (info) { + const receiver = visitNode(node.expression, visitor, isExpression); + + // TODO(aclaymore): Should this be abstracted into a factory function? + return setOriginalNode( + factory.createCallExpression( + factory.createPropertyAccessExpression( + info.weakMapName, + 'has' + ), + /* typeArguments: */ undefined, + [receiver] + ), + node + ) + } + + // Private name has not been declared. Subsequent transformers will handle this error + // TODO(aclaymore): confirm this is how we want to handle the error + return visitEachChild(node, visitor, context); + } + /** * Visits the members of a class that has fields. * diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 5ec6dcd7cc202..294c64eaa9cf5 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -795,7 +795,7 @@ namespace ts { case SyntaxKind.PrivateIdentifierInInExpression: return factory.updatePrivateIdentifierInInExpression(node, - nodeVisitor((node).name, visitor, isPrivateIdentifier), + nodeVisitor((node).name, visitor, isExpression), nodeVisitor((node).expression, visitor, isExpression)); case SyntaxKind.ConditionalExpression: diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt new file mode 100644 index 0000000000000..26f7e36d03615 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt @@ -0,0 +1,29 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(19,12): error TS18016: Private identifiers are not allowed outside class bodies. + + +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (1 errors) ==== + // TODO(aclaymore) check where transform cases live + // TODO(aclaymore) add cases for static fields + + class Foo { + #p1 = 1; + check(v: any) { + #p1 in v; // expect `_p1.has(v)` + } + } + + class Bar { + #p1 = 1; + check(v: any) { + #p1 in v; // expect `_p1_1.has(v)` + } + } + + function error(v: Foo) { + return #p1 in v; // expect `in v` + ~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + } + + export { } + \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.js b/tests/baselines/reference/privateNameInInExpressionTransform.js new file mode 100644 index 0000000000000..4a39328914ccd --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.js @@ -0,0 +1,51 @@ +//// [privateNameInInExpressionTransform.ts] +// TODO(aclaymore) check where transform cases live +// TODO(aclaymore) add cases for static fields + +class Foo { + #p1 = 1; + check(v: any) { + #p1 in v; // expect `_p1.has(v)` + } +} + +class Bar { + #p1 = 1; + check(v: any) { + #p1 in v; // expect `_p1_1.has(v)` + } +} + +function error(v: Foo) { + return #p1 in v; // expect `in v` +} + +export { } + + +//// [privateNameInInExpressionTransform.js] +// TODO(aclaymore) check where transform cases live +// TODO(aclaymore) add cases for static fields +var _p1, _p1_1; +class Foo { + constructor() { + _p1.set(this, 1); + } + check(v) { + _p1.has(v); // expect `_p1.has(v)` + } +} +(_p1 = new WeakMap()); +class Bar { + constructor() { + _p1_1.set(this, 1); + } + check(v) { + _p1_1.has(v); // expect `_p1_1.has(v)` + } +} +(_p1_1 = new WeakMap()); +function error(v) { + return ( in v); // expect `in v` +} +export {}; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.symbols b/tests/baselines/reference/privateNameInInExpressionTransform.symbols new file mode 100644 index 0000000000000..d0f1f1b0d4c6d --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.symbols @@ -0,0 +1,45 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === +// TODO(aclaymore) check where transform cases live +// TODO(aclaymore) add cases for static fields + +class Foo { +>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) + + #p1 = 1; +>#p1 : Symbol(Foo.#p1, Decl(privateNameInInExpressionTransform.ts, 3, 11)) + + check(v: any) { +>check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 4, 12)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 5, 10)) + + #p1 in v; // expect `_p1.has(v)` +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 5, 10)) + } +} + +class Bar { +>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 8, 1)) + + #p1 = 1; +>#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 10, 11)) + + check(v: any) { +>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 11, 12)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 10)) + + #p1 in v; // expect `_p1_1.has(v)` +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 10)) + } +} + +function error(v: Foo) { +>error : Symbol(error, Decl(privateNameInInExpressionTransform.ts, 15, 1)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 17, 15)) +>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) + + return #p1 in v; // expect `in v` +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 17, 15)) +} + +export { } + diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.types b/tests/baselines/reference/privateNameInInExpressionTransform.types new file mode 100644 index 0000000000000..4f60de066bc56 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.types @@ -0,0 +1,49 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === +// TODO(aclaymore) check where transform cases live +// TODO(aclaymore) add cases for static fields + +class Foo { +>Foo : Foo + + #p1 = 1; +>#p1 : number +>1 : 1 + + check(v: any) { +>check : (v: any) => void +>v : any + + #p1 in v; // expect `_p1.has(v)` +>#p1 in v : boolean +>v : any + } +} + +class Bar { +>Bar : Bar + + #p1 = 1; +>#p1 : number +>1 : 1 + + check(v: any) { +>check : (v: any) => void +>v : any + + #p1 in v; // expect `_p1_1.has(v)` +>#p1 in v : boolean +>v : any + } +} + +function error(v: Foo) { +>error : (v: Foo) => any +>v : Foo + + return #p1 in v; // expect `in v` +>#p1 in v : any +>v : Foo +} + +export { } + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts new file mode 100644 index 0000000000000..0cb8fa2b2fe68 --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -0,0 +1,24 @@ +// @target: es2020 + +// TODO(aclaymore) check where transform cases live +// TODO(aclaymore) add cases for static fields + +class Foo { + #p1 = 1; + check(v: any) { + #p1 in v; // expect `_p1.has(v)` + } +} + +class Bar { + #p1 = 1; + check(v: any) { + #p1 in v; // expect `_p1_1.has(v)` + } +} + +function error(v: Foo) { + return #p1 in v; // expect `in v` +} + +export { } From 17c95e6d2252a61124cdc754f178ccf03bdbdbc5 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 25 Feb 2021 18:33:34 +0000 Subject: [PATCH 08/29] fix linting errors Signed-off-by: Ashley Claymore --- src/compiler/transformers/classFields.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 0d78aadd0978d..a893d924f348f 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -218,13 +218,13 @@ namespace ts { factory.createCallExpression( factory.createPropertyAccessExpression( info.weakMapName, - 'has' + "has" ), /* typeArguments: */ undefined, [receiver] ), node - ) + ); } // Private name has not been declared. Subsequent transformers will handle this error From ec072dc620eef03f7d6fa888f07219fa01e8846c Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 26 Feb 2021 17:14:06 +0000 Subject: [PATCH 09/29] mark as used Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 2 ++ ...privateNameInInExpressionUnused.errors.txt | 18 ++++++++++++++ .../privateNameInInExpressionUnused.js | 24 +++++++++++++++++++ .../privateNameInInExpressionUnused.symbols | 24 +++++++++++++++++++ .../privateNameInInExpressionUnused.types | 23 ++++++++++++++++++ .../privateNameInInExpressionUnused.ts | 15 ++++++++++++ 6 files changed, 106 insertions(+) create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.errors.txt create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.js create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.symbols create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.types create mode 100644 tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 17613425ed03d..0591bb0e8d8e6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31348,6 +31348,8 @@ namespace ts { return anyType; } + markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); + const exp = node.expression; let rightType = checkExpression(exp, checkMode); if (rightType === silentNeverType) { diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt b/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt new file mode 100644 index 0000000000000..739a095d4a4fc --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts(4,22): error TS6133: 'unused' is declared but its value is never read. + + +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts (1 errors) ==== + // TODO(aclaymore): verify we want this behavior + + class Foo { + private readonly unused: undefined; // expect unused error + ~~~~~~ +!!! error TS6133: 'unused' is declared but its value is never read. + readonly #brand: undefined; // expect no error + + isFoo(v: any): v is Foo { + // This should count as using/reading '#p1' + return #brand in v; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.js b/tests/baselines/reference/privateNameInInExpressionUnused.js new file mode 100644 index 0000000000000..326c7938e692b --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.js @@ -0,0 +1,24 @@ +//// [privateNameInInExpressionUnused.ts] +// TODO(aclaymore): verify we want this behavior + +class Foo { + private readonly unused: undefined; // expect unused error + readonly #brand: undefined; // expect no error + + isFoo(v: any): v is Foo { + // This should count as using/reading '#p1' + return #brand in v; + } +} + + +//// [privateNameInInExpressionUnused.js] +"use strict"; +// TODO(aclaymore): verify we want this behavior +class Foo { + #brand; // expect no error + isFoo(v) { + // This should count as using/reading '#p1' + return (#brand in v); + } +} diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.symbols b/tests/baselines/reference/privateNameInInExpressionUnused.symbols new file mode 100644 index 0000000000000..f864e4f9d4e10 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts === +// TODO(aclaymore): verify we want this behavior + +class Foo { +>Foo : Symbol(Foo, Decl(privateNameInInExpressionUnused.ts, 0, 0)) + + private readonly unused: undefined; // expect unused error +>unused : Symbol(Foo.unused, Decl(privateNameInInExpressionUnused.ts, 2, 11)) + + readonly #brand: undefined; // expect no error +>#brand : Symbol(Foo.#brand, Decl(privateNameInInExpressionUnused.ts, 3, 39)) + + isFoo(v: any): v is Foo { +>isFoo : Symbol(Foo.isFoo, Decl(privateNameInInExpressionUnused.ts, 4, 31)) +>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 6, 10)) +>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 6, 10)) +>Foo : Symbol(Foo, Decl(privateNameInInExpressionUnused.ts, 0, 0)) + + // This should count as using/reading '#p1' + return #brand in v; +>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 6, 10)) + } +} + diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.types b/tests/baselines/reference/privateNameInInExpressionUnused.types new file mode 100644 index 0000000000000..5e28f680dadfb --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.types @@ -0,0 +1,23 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts === +// TODO(aclaymore): verify we want this behavior + +class Foo { +>Foo : Foo + + private readonly unused: undefined; // expect unused error +>unused : undefined + + readonly #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 '#p1' + return #brand in v; +>#brand in v : boolean +>v : any + } +} + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts new file mode 100644 index 0000000000000..9aa63e1eefd7c --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts @@ -0,0 +1,15 @@ +// @strict: true +// @noUnusedLocals: true +// @target: esnext + +// TODO(aclaymore): verify we want this behavior + +class Foo { + private readonly unused: undefined; // expect unused error + readonly #brand: undefined; // expect no error + + isFoo(v: any): v is Foo { + // This should count as using/reading '#p1' + return #brand in v; + } +} From 45e1b484f6c1f9db90dddca7dfdd85e7b170026b Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 5 Mar 2021 13:36:14 +0000 Subject: [PATCH 10/29] improve error message when inside a class Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 12 +++++++++--- .../reference/privateNameInInExpression.errors.txt | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0591bb0e8d8e6..7ba2eda881b37 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23238,6 +23238,8 @@ namespace ts { return type; } + // TODO(aclaymore): add logic for private static fields + const privateId = expr.name; const klass = lookupClassForPrivateIdentifierDeclaration(privateId); if (klass === undefined) { @@ -31343,8 +31345,13 @@ namespace ts { const privateId = node.name; const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); if (lexicallyScopedSymbol === undefined) { - // TODO(aclaymore): use better error message - we might be in a class but with no matching privateField - error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + if (!getContainingClass(node)) { + error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + } + else { + // TODO(aclamore): suggest similar name + error(node, Diagnostics.Cannot_find_name_0, diagnosticName(privateId)); + } return anyType; } @@ -31356,7 +31363,6 @@ namespace ts { return silentNeverType; } rightType = checkNonNullType(rightType, exp); - // TODO(aclaymore): Do RHS rules matching 'in' rules? (e.g. throw on null) checkInExpressionRHS(exp, rightType); return booleanType; } diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 870bbee56da6a..70d2482228bad 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(14,26): error TS2571: Object is of type 'unknown'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(16,19): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(16,19): error TS2304: Cannot find name '#p2'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,23): error TS1005: 'in' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS2304: Cannot find name '#p1'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. @@ -30,8 +30,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t !!! error TS2571: Object is of type 'unknown'. const f = #p2 in v; // Bad - Invalid privateID - ~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. + ~~~~~~~~ +!!! error TS2304: Cannot find name '#p2'. const g = (#p1) in v; // Bad - private id is not an expression on it's own From 9127d55fa2351947522e6eeba65b9da9e7fcb1f4 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 5 Mar 2021 16:11:31 +0000 Subject: [PATCH 11/29] respect whitespace and comments in emit Signed-off-by: Ashley Claymore --- src/compiler/emitter.ts | 21 ++-- src/compiler/factory/nodeFactory.ts | 8 +- src/compiler/parser.ts | 13 +- src/compiler/types.ts | 5 +- src/compiler/visitorPublic.ts | 1 + .../privateNameInInExpression.errors.txt | 35 ++---- .../reference/privateNameInInExpression.js | 75 +++++------ .../privateNameInInExpression.symbols | 118 ++++++++---------- .../reference/privateNameInInExpression.types | 51 ++------ ...vateNameInInExpressionTransform.errors.txt | 29 ++++- .../privateNameInInExpressionTransform.js | 33 ++++- ...privateNameInInExpressionTransform.symbols | 57 +++++++-- .../privateNameInInExpressionTransform.types | 48 ++++++- .../privateNameInInExpressionUnused.js | 2 +- .../privateNames/privateNameInInExpression.ts | 20 +-- .../privateNameInInExpressionTransform.ts | 16 ++- 16 files changed, 295 insertions(+), 237 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 558e5e97e6bc0..b9bee512f0f53 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2554,7 +2554,6 @@ namespace ts { state.declarationListContainerEndStack[state.stackIndex] = declarationListContainerEnd; const emitComments = state.shouldEmitCommentsStack[state.stackIndex] = shouldEmitComments(node); const emitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex] = shouldEmitSourceMaps(node); - writeToken(SyntaxKind.OpenParenToken, node.pos, writePunctuation); // TODO(aclaymore): remove - for debugging beforeEmitWithContext(node, emitComments, emitSourceMaps); } else { @@ -2594,7 +2593,6 @@ namespace ts { const shouldEmitComments = state.shouldEmitCommentsStack[state.stackIndex]; const shouldEmitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex]; afterEmitWithContext(node, shouldEmitComments, shouldEmitSourceMaps, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd, savedPreserveSourceNewlines); - writeToken(SyntaxKind.CloseParenToken, node.pos, writePunctuation); // TODO(aclaymore): remove - for debugging state.stackIndex--; } } @@ -2610,14 +2608,21 @@ namespace ts { } function emitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { - // TODO(aclaymore) - emit better. Temp adding parenthesis for debugging - writeToken(SyntaxKind.OpenParenToken, node.pos, writePunctuation); + const linesBeforeIn = getLinesBetweenNodes(node, node.name, node.inToken); + const linesAfterIn = getLinesBetweenNodes(node, node.inToken, node.expression); + + emitLeadingCommentsOfPosition(node.name.pos); emitPrivateIdentifier(node.name); - writeSpace(); - writeToken(SyntaxKind.InKeyword, node.pos, writePunctuation); - writeSpace(); + emitTrailingCommentsOfPosition(node.name.end); + + writeLinesAndIndent(linesBeforeIn, /*writeSpaceIfNotIndenting*/ true); + emitLeadingCommentsOfPosition(node.inToken.pos); + writeTokenNode(node.inToken, writeKeyword); + emitTrailingCommentsOfPosition(node.inToken.end, /*prefixSpace*/ true); + writeLinesAndIndent(linesAfterIn, /*writeSpaceIfNotIndenting*/ true); + emit(node.expression); - writeToken(SyntaxKind.CloseParenToken, node.pos, writePunctuation); + decreaseIndentIf(linesBeforeIn, linesAfterIn); } function emitConditionalExpression(node: ConditionalExpression) { diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index ae65c847eae35..85b39d7288542 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -3062,12 +3062,14 @@ namespace ts { } // @api - function createPrivateIdentifierInInExpression(name: PrivateIdentifier, expression: Expression) { + function createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression) { const node = createBaseExpression(SyntaxKind.PrivateIdentifierInInExpression); node.name = name; + node.inToken = inToken; node.expression = expression; node.transformFlags |= propagateChildFlags(node.name) | + propagateChildFlags(node.inToken) | propagateChildFlags(node.expression) | TransformFlags.ContainsESNext; return node; @@ -3077,11 +3079,13 @@ namespace ts { function updatePrivateIdentifierInInExpression( node: PrivateIdentifierInInExpression, name: PrivateIdentifier, + inToken: Token, expression: Expression ): PrivateIdentifierInInExpression { return node.name !== name + || node.inToken !== inToken || node.expression !== expression - ? update(createPrivateIdentifierInInExpression(name, expression), node) + ? update(createPrivateIdentifierInInExpression(name, inToken, expression), node) : node; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4b37609bd429c..887a3c034c221 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4434,6 +4434,9 @@ namespace ts { } function parsePrivateIdentifierInInExpression(pos: number): Expression { + // PrivateIdentifierInInExpression[in]: + // [+in] PrivateIdentifier in RelationalExpression[?in] + Debug.assert(token() === SyntaxKind.PrivateIdentifier, "parsePrivateIdentifierInInExpression should only have been called if we had a privateIdentifier"); Debug.assert(inDisallowInContext() === false, "parsePrivateIdentifierInInExpression should only have been called if 'in' is allowed"); const id = parsePrivateIdentifier(); @@ -4441,13 +4444,17 @@ namespace ts { // TODO(aclaymore) use better error return createMissingNode(SyntaxKind.InKeyword, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, tokenToString(SyntaxKind.InKeyword)); } - nextToken(); - // TODO(aclaymore) verify precedence is correct + + const inToken = parseTokenNode>(); const exp = parseBinaryExpressionOrHigher(OperatorPrecedence.Relational); - return finishNode(factory.createPrivateIdentifierInInExpression(id, exp), pos); + return finishNode(factory.createPrivateIdentifierInInExpression(id, inToken, exp), pos); } function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { + // parse a BinaryExpression the LHS is either: + // 1) a PrivateIdentifierInInExpression when 'in' flag allowed and lookahead matches a PrivateIdentifier + // 2) a UnaryExpression + const pos = getNodePos(); const tryPrivateIdentifierInIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext(); const leftOperand = tryPrivateIdentifierInIn diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 54d6b688cb5a4..39ef2b9f32770 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2309,6 +2309,7 @@ namespace ts { export interface PrivateIdentifierInInExpression extends Expression { readonly kind: SyntaxKind.PrivateIdentifierInInExpression; readonly name: PrivateIdentifier; + readonly inToken: Token; readonly expression: Expression; } @@ -7095,8 +7096,8 @@ namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; - createPrivateIdentifierInInExpression(name: PrivateIdentifier, expression: Expression): PrivateIdentifierInInExpression; - updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, expression: Expression): PrivateIdentifierInInExpression; + createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; + updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; // // Misc diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 294c64eaa9cf5..8246d4440d013 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -796,6 +796,7 @@ namespace ts { case SyntaxKind.PrivateIdentifierInInExpression: return factory.updatePrivateIdentifierInInExpression(node, nodeVisitor((node).name, visitor, isExpression), + nodeVisitor((node).inToken, tokenVisitor, isToken), nodeVisitor((node).expression, visitor, isExpression)); case SyntaxKind.ConditionalExpression: diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 70d2482228bad..a7831f5c26021 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -4,14 +4,11 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS2304: Cannot find name '#p1'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,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(34,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(34,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(36,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(46,24): error TS2531: Object is possibly 'null'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(99,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(36,24): error TS2531: Object is possibly 'null'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(89,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (11 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (8 errors) ==== // TODO(aclaymore) split up into separate cases class Foo { @@ -33,7 +30,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t ~~~~~~~~ !!! error TS2304: Cannot find name '#p2'. - const g = (#p1) in v; // Bad - private id is not an expression on it's own + const g = (#p1) in v; // Bad - private id is not an expression on its own !!! error TS1005: 'in' expected. @@ -50,26 +47,10 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid } - precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' - // '<<' has higher precedence than 'in' - - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) - - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) - ~~~~~~~~~~~~~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. - ~~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. - - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) - ~~~~~~~~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. - - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) - - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + whitespace(v: any) { + const a = v && /*0*/#p1/*1*/ + /*2*/in/*3*/ + /*4*/v/*5*/ } flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 71535bcc89f37..f4945c31b372b 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -16,7 +16,7 @@ class Foo { const f = #p2 in v; // Bad - Invalid privateID - const g = (#p1) in v; // Bad - private id is not an expression on it's own + const g = (#p1) in v; // Bad - private id is not an expression on its own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed @@ -25,20 +25,10 @@ class Foo { for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid } - precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' - // '<<' has higher precedence than 'in' - - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) - - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) - - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) - - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) - - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + whitespace(v: any) { + const a = v && /*0*/#p1/*1*/ + /*2*/in/*3*/ + /*4*/v/*5*/ } flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { @@ -107,41 +97,36 @@ export { } // TODO(aclaymore) split up into separate cases class Foo { constructor() { - (this.#p1 = 1); + this.#p1 = 1; } #p1; basics(v) { - const a = (#p1 in v); // Good - a is boolean - const b = (#p1 in v.p1.p2); // Good - b is boolean - const c = (#p1 in v); // Good - c is boolean - const d = (#p1 in v); // Good d is boolean (not true) - const e = (#p1 in v); // Bad - RHS of in must be object type or any - const f = (#p2 in v); // Bad - Invalid privateID - const g = (() in v); // Bad - private id is not an expression on it's own + const a = #p1 in v; // Good - a is boolean + const b = #p1 in v.p1.p2; // Good - b is boolean + const c = #p1 in v; // Good - c is boolean + const d = #p1 in v; // Good d is boolean (not true) + const e = #p1 in v; // Bad - RHS of in must be object type or any + const f = #p2 in v; // Bad - Invalid privateID + const g = () in v; // Bad - private id is not an expression on its own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed - for (let x in (#p1 in v)) { /* no-op */ } // Bad - rhs of in should be a object/any - for (let x in (#p1 in v)) { /* no-op */ } // Good - weird but valid + for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + for (let x in #p1 in v) { /* no-op */ } // Good - weird but valid } - precedence(v) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' - // '<<' has higher precedence than 'in' - ((v == (#p1 in v)) == v); // Good precedence: ((v == (#p1 in v)) == v) - (v << (#p1 in (v << v))); // Good precedence: (v << (#p1 in (v << v))) - ((v << (#p1 in v)) == v); // Good precedence: ((v << (#p1 in v)) == v) - (v == ((#p1 in v) < v)); // Good precedence: (v == ((#p1 in v) < v)) - ((#p1 in v) && (#p1 in v)); // Good precedence: ((#p1 in v) && (#p1 in v)) + whitespace(v) { + const a = v && /*0*/ #p1/*1*/ + /*2*/ in /*3*/ + /*4*/ v; /*5*/ } flow(u, fb, fs, b, fsb) { - if ((typeof u === 'object')) { - if ((#p1 in u)) { + if (typeof u === 'object') { + if (#p1 in u) { u; // good u is Foo } else { u; // good u is object | null } - if ((u !== null)) { - if ((#p1 in u)) { + if (u !== null) { + if (#p1 in u) { u; // good u is Foo } else { @@ -149,25 +134,25 @@ class Foo { } } } - if ((#p1 in fb)) { + if (#p1 in fb) { fb; // good fb is Foo } else { fb; // good fb is Bar } - if ((#p1 in fs)) { + if (#p1 in fs) { fs; // good fb is Foo (or FooSub?) } else { fs; // good fs is never } - if ((#p1 in b)) { + if (#p1 in b) { b; // good b is 'Bar & Foo' } else { b; // good b is Bar } - if ((#p1 in fsb)) { + if (#p1 in fsb) { fsb; // good fsb is FooSub } else { @@ -175,7 +160,7 @@ class Foo { } class Nested { m(v) { - if ((#p1 in v)) { + if (#p1 in v) { v; // good v is Foo } } @@ -186,10 +171,10 @@ class FooSub extends Foo { } class Bar { constructor() { - (this.notFoo = true); + this.notFoo = true; } } function error(v) { - return (#p1 in v); // Bad - outside of class + return #p1 in v; // Bad - outside of class } export {}; diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 0615eb7488bc5..1e095ab6bb7f3 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -36,7 +36,7 @@ class Foo { >f : Symbol(f, Decl(privateNameInInExpression.ts, 15, 13)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) - const g = (#p1) in v; // Bad - private id is not an expression on it's own + const g = (#p1) in v; // Bad - private id is not an expression on its own >g : Symbol(g, Decl(privateNameInInExpression.ts, 17, 13)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) @@ -52,138 +52,118 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) } - precedence(v: any) { ->precedence : Symbol(Foo.precedence, Decl(privateNameInInExpression.ts, 25, 5)) + whitespace(v: any) { +>whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 25, 5)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' - // '<<' has higher precedence than 'in' - - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) + const a = v && /*0*/#p1/*1*/ +>a : Symbol(a, Decl(privateNameInInExpression.ts, 27, 13)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) + /*2*/in/*3*/ + /*4*/v/*5*/ >v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) } flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { ->flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 40, 5)) ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) +>flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 30, 5)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 92, 1)) ->b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 92, 1)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 82, 1)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 82, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) if (typeof u === 'object') { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) if (#p1 in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) } else { u; // good u is object | null ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) } if (u !== null) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) if (#p1 in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) } else { u; // good u is object ->u : Symbol(u, Decl(privateNameInInExpression.ts, 41, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) } } } if (#p1 in fb) { ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) fb; // good fb is Foo ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) } else { fb; // good fb is Bar ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 41, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) } if (#p1 in fs) { ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) fs; // good fb is Foo (or FooSub?) ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) } else { fs; // good fs is never ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 41, 35)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) } if (#p1 in b) { ->b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) b; // good b is 'Bar & Foo' ->b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) } else { b; // good b is Bar ->b : Symbol(b, Decl(privateNameInInExpression.ts, 41, 47)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) } if (#p1 in fsb) { ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) fsb; // good fsb is FooSub ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) } else { fsb; // good fsb is Bar ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 41, 55)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) } class Nested { ->Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 82, 9)) +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 72, 9)) m(v: any) { ->m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 84, 22)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 85, 14)) +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 74, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 75, 14)) if (#p1 in v) { ->v : Symbol(v, Decl(privateNameInInExpression.ts, 85, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 75, 14)) v; // good v is Foo ->v : Symbol(v, Decl(privateNameInInExpression.ts, 85, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 75, 14)) } } } @@ -191,20 +171,20 @@ class Foo { } class FooSub extends Foo { } ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 92, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 82, 1)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) class Bar { notFoo = true } ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 94, 28)) ->notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 95, 11)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 85, 11)) function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpression.ts, 95, 27)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 97, 15)) +>error : Symbol(error, Decl(privateNameInInExpression.ts, 85, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 87, 15)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) return #p1 in v; // Bad - outside of class ->v : Symbol(v, Decl(privateNameInInExpression.ts, 97, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 87, 15)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 7553703c86b86..f0b87bdd74aff 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -52,7 +52,7 @@ class Foo { >#p2 in v : any >v : any - const g = (#p1) in v; // Bad - private id is not an expression on it's own + const g = (#p1) in v; // Bad - private id is not an expression on its own >g : boolean >(#p1) in v : boolean >(#p1) : any @@ -74,52 +74,19 @@ class Foo { >v : any } - precedence(v: any) { ->precedence : (v: any) => void + whitespace(v: any) { +>whitespace : (v: any) => void >v : any - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' - // '<<' has higher precedence than 'in' - - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) ->v == #p1 in v == v : boolean ->v == #p1 in v : boolean ->v : any ->#p1 in v : boolean ->v : any + const a = v && /*0*/#p1/*1*/ +>a : any +>v && /*0*/#p1/*1*/ /*2*/in/*3*/ /*4*/v : any >v : any +>#p1/*1*/ /*2*/in/*3*/ /*4*/v : boolean - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) ->v << #p1 in v << v : number ->v : any ->#p1 in v << v : boolean ->v << v : number ->v : any ->v : any - - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) ->v << #p1 in v == v : boolean ->v << #p1 in v : number ->v : any ->#p1 in v : boolean ->v : any + /*2*/in/*3*/ + /*4*/v/*5*/ >v : any - - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) ->v == #p1 in v < v : boolean ->v : any ->#p1 in v < v : boolean ->#p1 in v : boolean ->v : any ->v : any - - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) ->#p1 in v && #p1 in v : boolean ->#p1 in v : boolean ->v : any ->#p1 in v : boolean ->v : Foo } flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { >flow : (u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) => void diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt index 26f7e36d03615..4f1b93ac7f82d 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt @@ -1,8 +1,10 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(19,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(17,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(33,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (1 errors) ==== - // TODO(aclaymore) check where transform cases live +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (4 errors) ==== // TODO(aclaymore) add cases for static fields class Foo { @@ -10,6 +12,27 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr check(v: any) { #p1 in v; // expect `_p1.has(v)` } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) + + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) + ~~~~~~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + ~~~~~~ +!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. + + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) + ~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) + + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + } } class Bar { diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.js b/tests/baselines/reference/privateNameInInExpressionTransform.js index 4a39328914ccd..94c57db2d9f63 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.js +++ b/tests/baselines/reference/privateNameInInExpressionTransform.js @@ -1,5 +1,4 @@ //// [privateNameInInExpressionTransform.ts] -// TODO(aclaymore) check where transform cases live // TODO(aclaymore) add cases for static fields class Foo { @@ -7,6 +6,21 @@ class Foo { check(v: any) { #p1 in v; // expect `_p1.has(v)` } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) + + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) + + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) + + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) + + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + } } class Bar { @@ -24,7 +38,6 @@ export { } //// [privateNameInInExpressionTransform.js] -// TODO(aclaymore) check where transform cases live // TODO(aclaymore) add cases for static fields var _p1, _p1_1; class Foo { @@ -34,8 +47,18 @@ class Foo { check(v) { _p1.has(v); // expect `_p1.has(v)` } + precedence(v) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + v == _p1.has(v) == v; // Good precedence: ((v == (#p1 in v)) == v) + v << _p1.has(v << v); // Good precedence: (v << (#p1 in (v << v))) + v << _p1.has(v) == v; // Good precedence: ((v << (#p1 in v)) == v) + v == _p1.has(v) < v; // Good precedence: (v == ((#p1 in v) < v)) + _p1.has(v) && _p1.has(v); // Good precedence: ((#p1 in v) && (#p1 in v)) + } } -(_p1 = new WeakMap()); +_p1 = new WeakMap(); class Bar { constructor() { _p1_1.set(this, 1); @@ -44,8 +67,8 @@ class Bar { _p1_1.has(v); // expect `_p1_1.has(v)` } } -(_p1_1 = new WeakMap()); +_p1_1 = new WeakMap(); function error(v) { - return ( in v); // expect `in v` + return in v; // expect `in v` } export {}; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.symbols b/tests/baselines/reference/privateNameInInExpressionTransform.symbols index d0f1f1b0d4c6d..a5f4b13c84dfb 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform.symbols @@ -1,44 +1,75 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === -// TODO(aclaymore) check where transform cases live // TODO(aclaymore) add cases for static fields class Foo { >Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) #p1 = 1; ->#p1 : Symbol(Foo.#p1, Decl(privateNameInInExpressionTransform.ts, 3, 11)) +>#p1 : Symbol(Foo.#p1, Decl(privateNameInInExpressionTransform.ts, 2, 11)) check(v: any) { ->check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 4, 12)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 5, 10)) +>check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 3, 12)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) #p1 in v; // expect `_p1.has(v)` ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 5, 10)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) + } + precedence(v: any) { +>precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 6, 5)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) + + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) + + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) + + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) + + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) + + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) } } class Bar { ->Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 8, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 22, 1)) #p1 = 1; ->#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 10, 11)) +>#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 24, 11)) check(v: any) { ->check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 11, 12)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 10)) +>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 25, 12)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 26, 10)) #p1 in v; // expect `_p1_1.has(v)` ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 10)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 26, 10)) } } function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpressionTransform.ts, 15, 1)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 17, 15)) +>error : Symbol(error, Decl(privateNameInInExpressionTransform.ts, 29, 1)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 31, 15)) >Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) return #p1 in v; // expect `in v` ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 17, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 31, 15)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.types b/tests/baselines/reference/privateNameInInExpressionTransform.types index 4f60de066bc56..fa2eff91b33d2 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.types +++ b/tests/baselines/reference/privateNameInInExpressionTransform.types @@ -1,5 +1,4 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === -// TODO(aclaymore) check where transform cases live // TODO(aclaymore) add cases for static fields class Foo { @@ -17,6 +16,53 @@ class Foo { >#p1 in v : boolean >v : any } + precedence(v: any) { +>precedence : (v: any) => void +>v : any + + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) +>v == #p1 in v == v : boolean +>v == #p1 in v : boolean +>v : any +>#p1 in v : boolean +>v : any +>v : any + + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) +>v << #p1 in v << v : number +>v : any +>#p1 in v << v : boolean +>v << v : number +>v : any +>v : any + + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) +>v << #p1 in v == v : boolean +>v << #p1 in v : number +>v : any +>#p1 in v : boolean +>v : any +>v : any + + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) +>v == #p1 in v < v : boolean +>v : any +>#p1 in v < v : boolean +>#p1 in v : boolean +>v : any +>v : any + + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) +>#p1 in v && #p1 in v : boolean +>#p1 in v : boolean +>v : any +>#p1 in v : boolean +>v : Foo + } } class Bar { diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.js b/tests/baselines/reference/privateNameInInExpressionUnused.js index 326c7938e692b..feceadffc0bd0 100644 --- a/tests/baselines/reference/privateNameInInExpressionUnused.js +++ b/tests/baselines/reference/privateNameInInExpressionUnused.js @@ -19,6 +19,6 @@ class Foo { #brand; // expect no error isFoo(v) { // This should count as using/reading '#p1' - return (#brand in v); + return #brand in v; } } diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index ce90056a787cd..9567f355b730b 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -18,7 +18,7 @@ class Foo { const f = #p2 in v; // Bad - Invalid privateID - const g = (#p1) in v; // Bad - private id is not an expression on it's own + const g = (#p1) in v; // Bad - private id is not an expression on its own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed @@ -27,20 +27,10 @@ class Foo { for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid } - precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' - // '<<' has higher precedence than 'in' - - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) - - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) - - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) - - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) - - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + whitespace(v: any) { + const a = v && /*0*/#p1/*1*/ + /*2*/in/*3*/ + /*4*/v/*5*/ } flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index 0cb8fa2b2fe68..e4e47fc305374 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -1,6 +1,5 @@ // @target: es2020 -// TODO(aclaymore) check where transform cases live // TODO(aclaymore) add cases for static fields class Foo { @@ -8,6 +7,21 @@ class Foo { check(v: any) { #p1 in v; // expect `_p1.has(v)` } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) + + v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) + + v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) + + v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) + + #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + } } class Bar { From 4399cf51dbba01f7f7199ad0665a8a52a99f6742 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 9 Mar 2021 18:27:22 +0000 Subject: [PATCH 12/29] dont parse single privateIdentifier to reenable quick fix Signed-off-by: Ashley Claymore --- src/compiler/parser.ts | 9 +++++++-- .../reference/privateNameInInExpression.errors.txt | 11 +++++++---- .../baselines/reference/privateNameInInExpression.js | 2 +- .../reference/privateNameInInExpression.types | 1 + 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 887a3c034c221..82c3e777baf3d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4452,11 +4452,11 @@ namespace ts { function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { // parse a BinaryExpression the LHS is either: - // 1) a PrivateIdentifierInInExpression when 'in' flag allowed and lookahead matches a PrivateIdentifier + // 1) a PrivateIdentifierInInExpression when 'in' flag allowed and lookahead matches 'PrivateIdentifier in' // 2) a UnaryExpression const pos = getNodePos(); - const tryPrivateIdentifierInIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext(); + const tryPrivateIdentifierInIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext() && lookAhead(nextTokenIsInKeyword); const leftOperand = tryPrivateIdentifierInIn ? parsePrivateIdentifierInInExpression(pos) : parseUnaryExpressionOrHigher(); @@ -5930,6 +5930,11 @@ namespace ts { return (tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral || token() === SyntaxKind.StringLiteral) && !scanner.hasPrecedingLineBreak(); } + function nextTokenIsInKeyword() { + nextToken(); + return token() === SyntaxKind.InKeyword; + } + function isDeclaration(): boolean { while (true) { switch (token()) { diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index a7831f5c26021..fa3d8e74aba89 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,6 +1,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(14,26): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(16,19): error TS2304: Cannot find name '#p2'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,23): error TS1005: 'in' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,20): error TS2304: Cannot find name '#p1'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,20): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS2304: Cannot find name '#p1'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,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'. @@ -8,7 +9,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(89,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (8 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== // TODO(aclaymore) split up into separate cases class Foo { @@ -31,8 +32,10 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t !!! error TS2304: Cannot find name '#p2'. const g = (#p1) in v; // Bad - private id is not an expression on its own - -!!! error TS1005: 'in' expected. + ~~~ +!!! error TS2304: Cannot find name '#p1'. + ~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed ~~~ diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index f4945c31b372b..2274916f0b804 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -107,7 +107,7 @@ class Foo { const d = #p1 in v; // Good d is boolean (not true) const e = #p1 in v; // Bad - RHS of in must be object type or any const f = #p2 in v; // Bad - Invalid privateID - const g = () in v; // Bad - private id is not an expression on its own + const g = (#p1) in v; // Bad - private id is not an expression on its own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any for (let x in #p1 in v) { /* no-op */ } // Good - weird but valid diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index f0b87bdd74aff..bc5bdeebf46f8 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -56,6 +56,7 @@ class Foo { >g : boolean >(#p1) in v : boolean >(#p1) : any +>#p1 : any >v : any for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed From 04625959047017b2d60663bbbf05650f5d86efdb Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 9 Mar 2021 18:39:46 +0000 Subject: [PATCH 13/29] remove need for lookupClassForPrivateIdentifierDeclaration Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7ba2eda881b37..b918a674eaba1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23241,12 +23241,12 @@ namespace ts { // TODO(aclaymore): add logic for private static fields const privateId = expr.name; - const klass = lookupClassForPrivateIdentifierDeclaration(privateId); - if (klass === undefined) { + const symbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); + if (symbol === undefined) { return type; } - - const classType = getTypeOfSymbolAtLocation(getSymbolOfNode(klass), klass); + const classSymbol = symbol.parent!; + const classType = getTypeOfSymbol(classSymbol); const ctorSigs = getSignaturesOfType(classType, SignatureKind.Construct); // TODO(aclaymore): verify assertion is valid Debug.assert(ctorSigs.length > 0, "narrowTypeByPrivateIdentifierInInExpression should always find the class signature"); @@ -26912,17 +26912,6 @@ namespace ts { } } - function lookupClassForPrivateIdentifierDeclaration(id: PrivateIdentifier): ClassLikeDeclaration | undefined { - for (let containingClass = getContainingClass(id); !!containingClass; containingClass = getContainingClass(containingClass)) { - const { symbol } = containingClass; - const name = getSymbolNameForPrivateIdentifier(symbol, id.escapedText); - const prop = (symbol.members && symbol.members.get(name)) || (symbol.exports && symbol.exports.get(name)); - if (prop) { - return containingClass; - } - } - } - function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined { return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName); } From 23c8f982bb1c75f7d9b1d691498a68a900402ba2 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 9 Mar 2021 18:48:57 +0000 Subject: [PATCH 14/29] add tests for never Signed-off-by: Ashley Claymore --- .../privateNameInInExpression.errors.txt | 32 +++-- .../reference/privateNameInInExpression.js | 26 ++-- .../privateNameInInExpression.symbols | 118 ++++++++++-------- .../reference/privateNameInInExpression.types | 30 +++-- .../privateNames/privateNameInInExpression.ts | 14 ++- 5 files changed, 135 insertions(+), 85 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index fa3d8e74aba89..4cec7f9d9054f 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,12 +1,12 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(14,26): error TS2571: Object is of type 'unknown'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(16,19): error TS2304: Cannot find name '#p2'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,20): error TS2304: Cannot find name '#p1'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,20): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS2304: Cannot find name '#p1'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,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(36,24): error TS2531: Object is possibly 'null'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(89,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(16,26): error TS2571: Object is of type 'unknown'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,19): error TS2304: Cannot find name '#p2'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,20): error TS2304: Cannot find name '#p1'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,20): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,14): error TS2304: Cannot find name '#p1'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,14): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,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(42,24): error TS2531: Object is possibly 'null'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(95,12): error TS18016: Private identifiers are not allowed outside class bodies. ==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== @@ -23,15 +23,17 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const d = #p1 in (v as Foo); // Good d is boolean (not true) - const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const e = #p1 in (v as never); // Good e is boolean + + const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any ~~~~~~~~~~~~~~ !!! error TS2571: Object is of type 'unknown'. - const f = #p2 in v; // Bad - Invalid privateID + const g = #p2 in v; // Bad - Invalid privateID ~~~~~~~~ !!! error TS2304: Cannot find name '#p2'. - const g = (#p1) in v; // Bad - private id is not an expression on its own + const h = (#p1) in v; // Bad - private id is not an expression on its own ~~~ !!! error TS2304: Cannot find name '#p1'. ~~~ @@ -55,10 +57,14 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t /*2*/in/*3*/ /*4*/v/*5*/ } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { if (typeof u === 'object') { + if (#p1 in n) { + n; // good n is never + } + if (#p1 in u) { ~ !!! error TS2531: Object is possibly 'null'. diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 2274916f0b804..f1b06455e6bc5 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -12,11 +12,13 @@ class Foo { const d = #p1 in (v as Foo); // Good d is boolean (not true) - const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const e = #p1 in (v as never); // Good e is boolean - const f = #p2 in v; // Bad - Invalid privateID + const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any - const g = (#p1) in v; // Bad - private id is not an expression on its own + const g = #p2 in v; // Bad - Invalid privateID + + const h = (#p1) in v; // Bad - private id is not an expression on its own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed @@ -30,10 +32,14 @@ class Foo { /*2*/in/*3*/ /*4*/v/*5*/ } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { if (typeof u === 'object') { + if (#p1 in n) { + n; // good n is never + } + if (#p1 in u) { u; // good u is Foo } else { @@ -105,9 +111,10 @@ class Foo { const b = #p1 in v.p1.p2; // Good - b is boolean const c = #p1 in v; // Good - c is boolean const d = #p1 in v; // Good d is boolean (not true) - const e = #p1 in v; // Bad - RHS of in must be object type or any - const f = #p2 in v; // Bad - Invalid privateID - const g = (#p1) in v; // Bad - private id is not an expression on its own + const e = #p1 in v; // Good e is boolean + const f = #p1 in v; // Bad - RHS of in must be object type or any + const g = #p2 in v; // Bad - Invalid privateID + const h = (#p1) in v; // Bad - private id is not an expression on its own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any for (let x in #p1 in v) { /* no-op */ } // Good - weird but valid @@ -117,8 +124,11 @@ class Foo { /*2*/ in /*3*/ /*4*/ v; /*5*/ } - flow(u, fb, fs, b, fsb) { + flow(u, n, fb, fs, b, fsb) { if (typeof u === 'object') { + if (#p1 in n) { + n; // good n is never + } if (#p1 in u) { u; // good u is Foo } diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 1e095ab6bb7f3..85e1abe7600e2 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -28,142 +28,154 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) - const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const e = #p1 in (v as never); // Good e is boolean >e : Symbol(e, Decl(privateNameInInExpression.ts, 13, 13)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) - const f = #p2 in v; // Bad - Invalid privateID + const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any >f : Symbol(f, Decl(privateNameInInExpression.ts, 15, 13)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) - const g = (#p1) in v; // Bad - private id is not an expression on its own + const g = #p2 in v; // Bad - Invalid privateID >g : Symbol(g, Decl(privateNameInInExpression.ts, 17, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + + const h = (#p1) in v; // Bad - private id is not an expression on its own +>h : Symbol(h, Decl(privateNameInInExpression.ts, 19, 13)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any ->x : Symbol(x, Decl(privateNameInInExpression.ts, 21, 16)) +>x : Symbol(x, Decl(privateNameInInExpression.ts, 23, 16)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid ->x : Symbol(x, Decl(privateNameInInExpression.ts, 23, 16)) +>x : Symbol(x, Decl(privateNameInInExpression.ts, 25, 16)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) } whitespace(v: any) { ->whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 25, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 27, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 15)) const a = v && /*0*/#p1/*1*/ ->a : Symbol(a, Decl(privateNameInInExpression.ts, 27, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>a : Symbol(a, Decl(privateNameInInExpression.ts, 29, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 15)) /*2*/in/*3*/ /*4*/v/*5*/ ->v : Symbol(v, Decl(privateNameInInExpression.ts, 26, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 15)) } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { ->flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 30, 5)) ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { +>flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 32, 5)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) +>n : Symbol(n, Decl(privateNameInInExpression.ts, 33, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 82, 1)) ->b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 82, 1)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) if (typeof u === 'object') { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) + + if (#p1 in n) { +>n : Symbol(n, Decl(privateNameInInExpression.ts, 33, 20)) + + n; // good n is never +>n : Symbol(n, Decl(privateNameInInExpression.ts, 33, 20)) + } if (#p1 in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) } else { u; // good u is object | null ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) } if (u !== null) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) if (#p1 in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) } else { u; // good u is object ->u : Symbol(u, Decl(privateNameInInExpression.ts, 31, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) } } } if (#p1 in fb) { ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) fb; // good fb is Foo ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) } else { fb; // good fb is Bar ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 31, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) } if (#p1 in fs) { ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) fs; // good fb is Foo (or FooSub?) ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) } else { fs; // good fs is never ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 31, 35)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) } if (#p1 in b) { ->b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) b; // good b is 'Bar & Foo' ->b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) } else { b; // good b is Bar ->b : Symbol(b, Decl(privateNameInInExpression.ts, 31, 47)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) } if (#p1 in fsb) { ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) fsb; // good fsb is FooSub ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) } else { fsb; // good fsb is Bar ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 31, 55)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) } class Nested { ->Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 72, 9)) +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 78, 9)) m(v: any) { ->m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 74, 22)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 75, 14)) +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 80, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 81, 14)) if (#p1 in v) { ->v : Symbol(v, Decl(privateNameInInExpression.ts, 75, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 81, 14)) v; // good v is Foo ->v : Symbol(v, Decl(privateNameInInExpression.ts, 75, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 81, 14)) } } } @@ -171,20 +183,20 @@ class Foo { } class FooSub extends Foo { } ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 82, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) class Bar { notFoo = true } ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 84, 28)) ->notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 85, 11)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 91, 11)) function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpression.ts, 85, 27)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 87, 15)) +>error : Symbol(error, Decl(privateNameInInExpression.ts, 91, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 93, 15)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) return #p1 in v; // Bad - outside of class ->v : Symbol(v, Decl(privateNameInInExpression.ts, 87, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 93, 15)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index bc5bdeebf46f8..22f8863ca5f44 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -40,20 +40,27 @@ class Foo { >v as Foo : Foo >v : any - const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const e = #p1 in (v as never); // Good e is boolean >e : boolean +>#p1 in (v as never) : boolean +>(v as never) : never +>v as never : never +>v : any + + const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any +>f : boolean >#p1 in (v as unknown) : boolean >(v as unknown) : unknown >v as unknown : unknown >v : any - const f = #p2 in v; // Bad - Invalid privateID ->f : any + const g = #p2 in v; // Bad - Invalid privateID +>g : any >#p2 in v : any >v : any - const g = (#p1) in v; // Bad - private id is not an expression on its own ->g : boolean + const h = (#p1) in v; // Bad - private id is not an expression on its own +>h : boolean >(#p1) in v : boolean >(#p1) : any >#p1 : any @@ -89,9 +96,10 @@ class Foo { /*4*/v/*5*/ >v : any } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { ->flow : (u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) => void + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { +>flow : (u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) => void >u : unknown +>n : never >fb : Foo | Bar >fs : FooSub >b : Bar @@ -103,6 +111,14 @@ class Foo { >u : unknown >'object' : "object" + if (#p1 in n) { +>#p1 in n : boolean +>n : never + + n; // good n is never +>n : never + } + if (#p1 in u) { >#p1 in u : boolean >u : object | null diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 9567f355b730b..53686130a2703 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -14,11 +14,13 @@ class Foo { const d = #p1 in (v as Foo); // Good d is boolean (not true) - const e = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const e = #p1 in (v as never); // Good e is boolean - const f = #p2 in v; // Bad - Invalid privateID + const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any - const g = (#p1) in v; // Bad - private id is not an expression on its own + const g = #p2 in v; // Bad - Invalid privateID + + const h = (#p1) in v; // Bad - private id is not an expression on its own for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed @@ -32,10 +34,14 @@ class Foo { /*2*/in/*3*/ /*4*/v/*5*/ } - flow(u: unknown, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { if (typeof u === 'object') { + if (#p1 in n) { + n; // good n is never + } + if (#p1 in u) { u; // good u is Foo } else { From 43a7d9fe3739e40fa1c1a5448c129baea96ad27b Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 9 Mar 2021 18:54:31 +0000 Subject: [PATCH 15/29] give child class different structure from parent Signed-off-by: Ashley Claymore --- .../reference/privateNameInInExpression.errors.txt | 4 ++-- .../reference/privateNameInInExpression.js | 10 +++++++--- .../reference/privateNameInInExpression.symbols | 13 +++++++------ .../reference/privateNameInInExpression.types | 8 +++++--- .../privateNames/privateNameInInExpression.ts | 4 ++-- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 4cec7f9d9054f..73cf465886c92 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -89,7 +89,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t } if (#p1 in fs) { - fs; // good fb is Foo (or FooSub?) + fs; // good fs is FooSub } else { fs; // good fs is never } @@ -116,7 +116,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t } } - class FooSub extends Foo { } + class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } function error(v: Foo) { diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index f1b06455e6bc5..b9f4ed20bc49a 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -62,7 +62,7 @@ class Foo { } if (#p1 in fs) { - fs; // good fb is Foo (or FooSub?) + fs; // good fs is FooSub } else { fs; // good fs is never } @@ -89,7 +89,7 @@ class Foo { } } -class FooSub extends Foo { } +class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } function error(v: Foo) { @@ -151,7 +151,7 @@ class Foo { fb; // good fb is Bar } if (#p1 in fs) { - fs; // good fb is Foo (or FooSub?) + fs; // good fs is FooSub } else { fs; // good fs is never @@ -178,6 +178,10 @@ class Foo { } } class FooSub extends Foo { + constructor() { + super(...arguments); + this.subTypeOfFoo = true; + } } class Bar { constructor() { diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 85e1abe7600e2..f79c86b1135df 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -74,14 +74,14 @@ class Foo { >n : Symbol(n, Decl(privateNameInInExpression.ts, 33, 20)) >fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) >fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) >FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) >b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) >fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) >FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) if (typeof u === 'object') { >u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) @@ -134,7 +134,7 @@ class Foo { if (#p1 in fs) { >fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) - fs; // good fb is Foo (or FooSub?) + fs; // good fs is FooSub >fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) } else { @@ -182,12 +182,13 @@ class Foo { } } -class FooSub extends Foo { } +class FooSub extends Foo { subTypeOfFoo = true } >FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) +>subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 90, 26)) class Bar { notFoo = true } ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 28)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) >notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 91, 11)) function error(v: Foo) { diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 22f8863ca5f44..6221fa88a89e6 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -166,8 +166,8 @@ class Foo { >#p1 in fs : boolean >fs : FooSub - fs; // good fb is Foo (or FooSub?) ->fs : Foo + fs; // good fs is FooSub +>fs : FooSub } else { fs; // good fs is never @@ -217,9 +217,11 @@ class Foo { } } -class FooSub extends Foo { } +class FooSub extends Foo { subTypeOfFoo = true } >FooSub : FooSub >Foo : Foo +>subTypeOfFoo : boolean +>true : true class Bar { notFoo = true } >Bar : Bar diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 53686130a2703..dc70f6c260429 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -64,7 +64,7 @@ class Foo { } if (#p1 in fs) { - fs; // good fb is Foo (or FooSub?) + fs; // good fs is FooSub } else { fs; // good fs is never } @@ -91,7 +91,7 @@ class Foo { } } -class FooSub extends Foo { } +class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } function error(v: Foo) { From 170e61bcd44923b7732fe262890e593956cd30b2 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 9 Mar 2021 19:19:08 +0000 Subject: [PATCH 16/29] cleanup testing for unused field Signed-off-by: Ashley Claymore --- ...privateNameInInExpressionUnused.errors.txt | 14 ++++++------- .../privateNameInInExpressionUnused.js | 12 +++++------ .../privateNameInInExpressionUnused.symbols | 20 +++++++++---------- .../privateNameInInExpressionUnused.types | 10 ++++------ .../privateNameInInExpressionUnused.ts | 8 +++----- 5 files changed, 27 insertions(+), 37 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt b/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt index 739a095d4a4fc..94d56876d297b 100644 --- a/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt @@ -1,17 +1,15 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts(4,22): error TS6133: 'unused' is declared but its value is never read. +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) ==== - // TODO(aclaymore): verify we want this behavior - class Foo { - private readonly unused: undefined; // expect unused error - ~~~~~~ -!!! error TS6133: 'unused' is declared but its value is never read. - readonly #brand: undefined; // expect no error + #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 '#p1' + // This should count as using/reading '#brand' return #brand in v; } } diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.js b/tests/baselines/reference/privateNameInInExpressionUnused.js index feceadffc0bd0..651f2932d1669 100644 --- a/tests/baselines/reference/privateNameInInExpressionUnused.js +++ b/tests/baselines/reference/privateNameInInExpressionUnused.js @@ -1,12 +1,10 @@ //// [privateNameInInExpressionUnused.ts] -// TODO(aclaymore): verify we want this behavior - class Foo { - private readonly unused: undefined; // expect unused error - readonly #brand: undefined; // expect no error + #unused: undefined; // expect unused error + #brand: undefined; // expect no error isFoo(v: any): v is Foo { - // This should count as using/reading '#p1' + // This should count as using/reading '#brand' return #brand in v; } } @@ -14,11 +12,11 @@ class Foo { //// [privateNameInInExpressionUnused.js] "use strict"; -// TODO(aclaymore): verify we want this behavior class Foo { + #unused; // expect unused error #brand; // expect no error isFoo(v) { - // This should count as using/reading '#p1' + // This should count as using/reading '#brand' return #brand in v; } } diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.symbols b/tests/baselines/reference/privateNameInInExpressionUnused.symbols index f864e4f9d4e10..03c516ae887f9 100644 --- a/tests/baselines/reference/privateNameInInExpressionUnused.symbols +++ b/tests/baselines/reference/privateNameInInExpressionUnused.symbols @@ -1,24 +1,22 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts === -// TODO(aclaymore): verify we want this behavior - class Foo { >Foo : Symbol(Foo, Decl(privateNameInInExpressionUnused.ts, 0, 0)) - private readonly unused: undefined; // expect unused error ->unused : Symbol(Foo.unused, Decl(privateNameInInExpressionUnused.ts, 2, 11)) + #unused: undefined; // expect unused error +>#unused : Symbol(Foo.#unused, Decl(privateNameInInExpressionUnused.ts, 0, 11)) - readonly #brand: undefined; // expect no error ->#brand : Symbol(Foo.#brand, Decl(privateNameInInExpressionUnused.ts, 3, 39)) + #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, 4, 31)) ->v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 6, 10)) ->v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 6, 10)) +>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 '#p1' + // This should count as using/reading '#brand' return #brand in v; ->v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 6, 10)) +>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10)) } } diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.types b/tests/baselines/reference/privateNameInInExpressionUnused.types index 5e28f680dadfb..74eea43e013b8 100644 --- a/tests/baselines/reference/privateNameInInExpressionUnused.types +++ b/tests/baselines/reference/privateNameInInExpressionUnused.types @@ -1,20 +1,18 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts === -// TODO(aclaymore): verify we want this behavior - class Foo { >Foo : Foo - private readonly unused: undefined; // expect unused error ->unused : undefined + #unused: undefined; // expect unused error +>#unused : undefined - readonly #brand: undefined; // expect no error + #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 '#p1' + // This should count as using/reading '#brand' return #brand in v; >#brand in v : boolean >v : any diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts index 9aa63e1eefd7c..4b214a2b9e78f 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts @@ -2,14 +2,12 @@ // @noUnusedLocals: true // @target: esnext -// TODO(aclaymore): verify we want this behavior - class Foo { - private readonly unused: undefined; // expect unused error - readonly #brand: undefined; // expect no error + #unused: undefined; // expect unused error + #brand: undefined; // expect no error isFoo(v: any): v is Foo { - // This should count as using/reading '#p1' + // This should count as using/reading '#brand' return #brand in v; } } From 7e3336595632163bc3baa8bc04d4863525f477fb Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 9 Mar 2021 19:24:45 +0000 Subject: [PATCH 17/29] use isTypeDerivedFrom Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b918a674eaba1..8d194760c543a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23251,7 +23251,7 @@ namespace ts { // TODO(aclaymore): verify assertion is valid Debug.assert(ctorSigs.length > 0, "narrowTypeByPrivateIdentifierInInExpression should always find the class signature"); const instanceType = getReturnTypeOfSignature(ctorSigs[0]); - return getNarrowedType(type, instanceType, assumeTrue, isTypeSubtypeOf); + return getNarrowedType(type, instanceType, assumeTrue, isTypeDerivedFrom); } function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { From f2efbcf748a21aae545fe36c51b5421f9151a2bf Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 9 Mar 2021 19:29:00 +0000 Subject: [PATCH 18/29] add TODO for static private field emit Signed-off-by: Ashley Claymore --- src/compiler/transformers/classFields.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index a893d924f348f..d532063419e96 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -213,6 +213,8 @@ namespace ts { if (info) { const receiver = visitNode(node.expression, visitor, isExpression); + // TODO(aclaymore): will need to change emit for static private fields + // TODO(aclaymore): Should this be abstracted into a factory function? return setOriginalNode( factory.createCallExpression( From 598215b43e87bb32df7bce9843c42d8afb2fab0c Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 15 Mar 2021 11:19:00 +0000 Subject: [PATCH 19/29] include in find-all-references results Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 10 ++++++++++ .../fourslash/findAllRefsPrivateNameProperties.ts | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8d194760c543a..86f3eb671b08e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31345,6 +31345,7 @@ namespace ts { } markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); + getNodeLinks(node).resolvedSymbol = lexicallyScopedSymbol; const exp = node.expression; let rightType = checkExpression(exp, checkMode); @@ -38979,6 +38980,15 @@ namespace ts { return resolveEntityName(name, /*meaning*/ SymbolFlags.FunctionScopedVariable); } + if (isPrivateIdentifier(name) && isPrivateIdentifierInInExpression(name.parent)) { + const links = getNodeLinks(name.parent); + if (links.resolvedSymbol) { + return links.resolvedSymbol; + } + checkPrivateIdentifierInInExpression(name.parent); + return links.resolvedSymbol; + } + // Do we want to return undefined here? return undefined; } diff --git a/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts b/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts index 8470772e9bf52..06c886b7e4468 100644 --- a/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts +++ b/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts @@ -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]); From f7ff235ea3d87155c93e3b54a643207eb2c84e49 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 15 Mar 2021 13:04:40 +0000 Subject: [PATCH 20/29] emit as helper with runtime type check Signed-off-by: Ashley Claymore --- src/compiler/factory/emitHelpers.ts | 21 ++++++++++ src/compiler/transformers/classFields.ts | 12 +----- ...vateNameInInExpressionTransform.errors.txt | 22 +++++++--- .../privateNameInInExpressionTransform.js | 42 +++++++++++++------ ...privateNameInInExpressionTransform.symbols | 34 +++++++++------ .../privateNameInInExpressionTransform.types | 25 ++++++++--- .../privateNameInInExpressionTransform.ts | 12 ++++-- 7 files changed, 117 insertions(+), 51 deletions(-) diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index f3e0004efd407..eca0b8500100f 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -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(receiver: Expression, privateField: Identifier): Expression; } export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory { @@ -72,6 +73,7 @@ namespace ts { // Class Fields Helpers createClassPrivateFieldGetHelper, createClassPrivateFieldSetHelper, + createClassPrivateFieldInHelper }; /** @@ -392,6 +394,11 @@ namespace ts { return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, args); } + function createClassPrivateFieldInHelper(receiver: Expression, privateField: Identifier) { + // TODO(aclaymore): will need to change emit for static private fields + context.requestEmitHelper(classPrivateFieldInHelper); + return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [receiver, privateField]); + } } /* @internal */ @@ -954,6 +961,19 @@ namespace ts { };` }; + export const classPrivateFieldInHelper: UnscopedEmitHelper = { + name: "typescript:classPrivateFieldIn", + importName: "__classPrivateFieldIn", + scoped: false, + text: ` + var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, privateMap) { + if (receiver === null || (typeof receiver !== 'object' && typeof receiver !== 'function')) { + throw new TypeError("Cannot use 'in' operator on non-object"); + } + return privateMap.has(receiver); + };` + }; + let allUnscopedEmitHelpers: ReadonlyESMap | undefined; export function getAllUnscopedEmitHelpers() { @@ -979,6 +999,7 @@ namespace ts { exportStarHelper, classPrivateFieldGetHelper, classPrivateFieldSetHelper, + classPrivateFieldInHelper, createBindingHelper, setModuleDefaultHelper ], helper => helper.name)); diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index d532063419e96..362ab254d76e8 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -213,18 +213,8 @@ namespace ts { if (info) { const receiver = visitNode(node.expression, visitor, isExpression); - // TODO(aclaymore): will need to change emit for static private fields - - // TODO(aclaymore): Should this be abstracted into a factory function? return setOriginalNode( - factory.createCallExpression( - factory.createPropertyAccessExpression( - info.weakMapName, - "has" - ), - /* typeArguments: */ undefined, - [receiver] - ), + context.getEmitHelperFactory().createClassPrivateFieldInHelper(receiver, info.weakMapName), node ); } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt index 4f1b93ac7f82d..bec2de1729cbf 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt @@ -1,16 +1,18 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(17,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(33,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,21): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(25,18): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(37,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (4 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (6 errors) ==== // TODO(aclaymore) add cases for static fields class Foo { #p1 = 1; check(v: any) { - #p1 in v; // expect `_p1.has(v)` + #p1 in v; // expect WeakMap '_p1' } precedence(v: any) { // '==' has lower precedence than 'in' @@ -33,17 +35,25 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } + invalidLHS(v: any) { + 'prop' in v = 10; + ~ +!!! error TS1005: ';' expected. + #p1 in v = 10; + ~ +!!! error TS1005: ';' expected. + } } class Bar { #p1 = 1; check(v: any) { - #p1 in v; // expect `_p1_1.has(v)` + #p1 in v; // expect WeakMap '_p1_1' } } - function error(v: Foo) { - return #p1 in v; // expect `in v` + function syntaxError(v: Foo) { + return #p1 in v; // expect `return in v` so runtime will have a syntax error ~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.js b/tests/baselines/reference/privateNameInInExpressionTransform.js index 94c57db2d9f63..9ab344c9a07bd 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.js +++ b/tests/baselines/reference/privateNameInInExpressionTransform.js @@ -4,7 +4,7 @@ class Foo { #p1 = 1; check(v: any) { - #p1 in v; // expect `_p1.has(v)` + #p1 in v; // expect WeakMap '_p1' } precedence(v: any) { // '==' has lower precedence than 'in' @@ -21,17 +21,21 @@ class Foo { #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } + invalidLHS(v: any) { + 'prop' in v = 10; + #p1 in v = 10; + } } class Bar { #p1 = 1; check(v: any) { - #p1 in v; // expect `_p1_1.has(v)` + #p1 in v; // expect WeakMap '_p1_1' } } -function error(v: Foo) { - return #p1 in v; // expect `in v` +function syntaxError(v: Foo) { + return #p1 in v; // expect `return in v` so runtime will have a syntax error } export { } @@ -39,23 +43,35 @@ export { } //// [privateNameInInExpressionTransform.js] // TODO(aclaymore) add cases for static fields +var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, privateMap) { + if (receiver === null || (typeof receiver !== 'object' && typeof receiver !== 'function')) { + throw new TypeError("Cannot use 'in' operator on non-object"); + } + return privateMap.has(receiver); +}; var _p1, _p1_1; class Foo { constructor() { _p1.set(this, 1); } check(v) { - _p1.has(v); // expect `_p1.has(v)` + __classPrivateFieldIn(v, _p1); // expect WeakMap '_p1' } precedence(v) { // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == _p1.has(v) == v; // Good precedence: ((v == (#p1 in v)) == v) - v << _p1.has(v << v); // Good precedence: (v << (#p1 in (v << v))) - v << _p1.has(v) == v; // Good precedence: ((v << (#p1 in v)) == v) - v == _p1.has(v) < v; // Good precedence: (v == ((#p1 in v) < v)) - _p1.has(v) && _p1.has(v); // Good precedence: ((#p1 in v) && (#p1 in v)) + v == __classPrivateFieldIn(v, _p1) == v; // Good precedence: ((v == (#p1 in v)) == v) + v << __classPrivateFieldIn(v << v, _p1); // Good precedence: (v << (#p1 in (v << v))) + v << __classPrivateFieldIn(v, _p1) == v; // Good precedence: ((v << (#p1 in v)) == v) + v == __classPrivateFieldIn(v, _p1) < v; // Good precedence: (v == ((#p1 in v) < v)) + __classPrivateFieldIn(v, _p1) && __classPrivateFieldIn(v, _p1); // Good precedence: ((#p1 in v) && (#p1 in v)) + } + invalidLHS(v) { + 'prop' in v; + 10; + __classPrivateFieldIn(v, _p1); + 10; } } _p1 = new WeakMap(); @@ -64,11 +80,11 @@ class Bar { _p1_1.set(this, 1); } check(v) { - _p1_1.has(v); // expect `_p1_1.has(v)` + __classPrivateFieldIn(v, _p1_1); // expect WeakMap '_p1_1' } } _p1_1 = new WeakMap(); -function error(v) { - return in v; // expect `in v` +function syntaxError(v) { + return in v; // expect `return in v` so runtime will have a syntax error } export {}; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.symbols b/tests/baselines/reference/privateNameInInExpressionTransform.symbols index a5f4b13c84dfb..961cc80dd859f 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform.symbols @@ -11,7 +11,7 @@ class Foo { >check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 3, 12)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) - #p1 in v; // expect `_p1.has(v)` + #p1 in v; // expect WeakMap '_p1' >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) } precedence(v: any) { @@ -46,30 +46,40 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) } + invalidLHS(v: any) { +>invalidLHS : Symbol(Foo.invalidLHS, Decl(privateNameInInExpressionTransform.ts, 21, 5)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15)) + + 'prop' in v = 10; +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15)) + + #p1 in v = 10; +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15)) + } } class Bar { ->Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 22, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 26, 1)) #p1 = 1; ->#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 24, 11)) +>#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 28, 11)) check(v: any) { ->check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 25, 12)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 26, 10)) +>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 29, 12)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10)) - #p1 in v; // expect `_p1_1.has(v)` ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 26, 10)) + #p1 in v; // expect WeakMap '_p1_1' +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10)) } } -function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpressionTransform.ts, 29, 1)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 31, 15)) +function syntaxError(v: Foo) { +>syntaxError : Symbol(syntaxError, Decl(privateNameInInExpressionTransform.ts, 33, 1)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 21)) >Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) - return #p1 in v; // expect `in v` ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 31, 15)) + return #p1 in v; // expect `return in v` so runtime will have a syntax error +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 21)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.types b/tests/baselines/reference/privateNameInInExpressionTransform.types index fa2eff91b33d2..553df1ce69058 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.types +++ b/tests/baselines/reference/privateNameInInExpressionTransform.types @@ -12,7 +12,7 @@ class Foo { >check : (v: any) => void >v : any - #p1 in v; // expect `_p1.has(v)` + #p1 in v; // expect WeakMap '_p1' >#p1 in v : boolean >v : any } @@ -63,6 +63,21 @@ class Foo { >#p1 in v : boolean >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 + + #p1 in v = 10; +>#p1 in v : boolean +>v : any +>10 : 10 + } } class Bar { @@ -76,17 +91,17 @@ class Bar { >check : (v: any) => void >v : any - #p1 in v; // expect `_p1_1.has(v)` + #p1 in v; // expect WeakMap '_p1_1' >#p1 in v : boolean >v : any } } -function error(v: Foo) { ->error : (v: Foo) => any +function syntaxError(v: Foo) { +>syntaxError : (v: Foo) => any >v : Foo - return #p1 in v; // expect `in v` + return #p1 in v; // expect `return in v` so runtime will have a syntax error >#p1 in v : any >v : Foo } diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index e4e47fc305374..bbef9a83230f5 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -5,7 +5,7 @@ class Foo { #p1 = 1; check(v: any) { - #p1 in v; // expect `_p1.has(v)` + #p1 in v; // expect WeakMap '_p1' } precedence(v: any) { // '==' has lower precedence than 'in' @@ -22,17 +22,21 @@ class Foo { #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) } + invalidLHS(v: any) { + 'prop' in v = 10; + #p1 in v = 10; + } } class Bar { #p1 = 1; check(v: any) { - #p1 in v; // expect `_p1_1.has(v)` + #p1 in v; // expect WeakMap '_p1_1' } } -function error(v: Foo) { - return #p1 in v; // expect `in v` +function syntaxError(v: Foo) { + return #p1 in v; // expect `return in v` so runtime will have a syntax error } export { } From f3d1e5274f3a88d895f4920102bd10cbd2db91c7 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 29 Mar 2021 13:04:02 +0100 Subject: [PATCH 21/29] correct issues caused by rebase Signed-off-by: Ashley Claymore --- src/compiler/emitter.ts | 7 +++++ src/compiler/factory/nodeTests.ts | 5 ++++ src/compiler/transformers/classFields.ts | 4 +-- src/compiler/types.ts | 1 + ...vateNameInInExpressionTransform.errors.txt | 4 +-- .../privateNameInInExpressionTransform.js | 30 +++++++++---------- ...privateNameInInExpressionTransform.symbols | 4 +-- .../privateNameInInExpressionTransform.types | 4 +-- .../privateNameInInExpressionTransform.ts | 4 +-- 9 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b9bee512f0f53..669bdbec2f657 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -6555,6 +6555,13 @@ namespace ts { case SyntaxKind.BinaryExpression: return preprintBinaryExpression(node as BinaryExpression); + case SyntaxKind.PrivateIdentifierInInExpression: + Debug.type(node); + return factory.updatePrivateIdentifierInInExpression(node, + visitMemberName(node.name, isPrivateIdentifier), + visit(node.inToken, isInKeyword), + visitExpression(node.expression)); + case SyntaxKind.ConditionalExpression: Debug.type(node); return factory.updateConditionalExpression(node, diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 6001d909cd29f..d76ac3929de83 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -87,6 +87,11 @@ namespace ts { return node.kind === SyntaxKind.EqualsGreaterThanToken; } + /*@internal*/ + export function isInKeyword(node: Node): node is InKeyword { + return node.kind === SyntaxKind.InKeyword; + } + // Identifiers export function isIdentifier(node: Node): node is Identifier { diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 362ab254d76e8..476ce7eec7569 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -206,7 +206,7 @@ namespace ts { * Visits `#id in expr` */ function visitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { - if (!shouldTransformPrivateFields) { + if (!shouldTransformPrivateElements) { return node; } const info = accessPrivateIdentifier(node.name); @@ -214,7 +214,7 @@ namespace ts { const receiver = visitNode(node.expression, visitor, isExpression); return setOriginalNode( - context.getEmitHelperFactory().createClassPrivateFieldInHelper(receiver, info.weakMapName), + context.getEmitHelperFactory().createClassPrivateFieldInHelper(receiver, info.brandCheckIdentifier), node ); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 39ef2b9f32770..219aad8b10e3a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1013,6 +1013,7 @@ namespace ts { export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; + export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt index bec2de1729cbf..8f7b684463feb 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt @@ -12,7 +12,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr class Foo { #p1 = 1; check(v: any) { - #p1 in v; // expect WeakMap '_p1' + #p1 in v; // expect WeakMap '_Foo_p1' } precedence(v: any) { // '==' has lower precedence than 'in' @@ -48,7 +48,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr class Bar { #p1 = 1; check(v: any) { - #p1 in v; // expect WeakMap '_p1_1' + #p1 in v; // expect WeakMap '_Bar_p1' } } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.js b/tests/baselines/reference/privateNameInInExpressionTransform.js index 9ab344c9a07bd..cf299279d699f 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.js +++ b/tests/baselines/reference/privateNameInInExpressionTransform.js @@ -4,7 +4,7 @@ class Foo { #p1 = 1; check(v: any) { - #p1 in v; // expect WeakMap '_p1' + #p1 in v; // expect WeakMap '_Foo_p1' } precedence(v: any) { // '==' has lower precedence than 'in' @@ -30,7 +30,7 @@ class Foo { class Bar { #p1 = 1; check(v: any) { - #p1 in v; // expect WeakMap '_p1_1' + #p1 in v; // expect WeakMap '_Bar_p1' } } @@ -49,41 +49,41 @@ var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(rec } return privateMap.has(receiver); }; -var _p1, _p1_1; +var _Foo_p1, _Bar_p1; class Foo { constructor() { - _p1.set(this, 1); + _Foo_p1.set(this, 1); } check(v) { - __classPrivateFieldIn(v, _p1); // expect WeakMap '_p1' + __classPrivateFieldIn(v, _Foo_p1); // expect WeakMap '_Foo_p1' } precedence(v) { // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == __classPrivateFieldIn(v, _p1) == v; // Good precedence: ((v == (#p1 in v)) == v) - v << __classPrivateFieldIn(v << v, _p1); // Good precedence: (v << (#p1 in (v << v))) - v << __classPrivateFieldIn(v, _p1) == v; // Good precedence: ((v << (#p1 in v)) == v) - v == __classPrivateFieldIn(v, _p1) < v; // Good precedence: (v == ((#p1 in v) < v)) - __classPrivateFieldIn(v, _p1) && __classPrivateFieldIn(v, _p1); // Good precedence: ((#p1 in v) && (#p1 in v)) + v == __classPrivateFieldIn(v, _Foo_p1) == v; // Good precedence: ((v == (#p1 in v)) == v) + v << __classPrivateFieldIn(v << v, _Foo_p1); // Good precedence: (v << (#p1 in (v << v))) + v << __classPrivateFieldIn(v, _Foo_p1) == v; // Good precedence: ((v << (#p1 in v)) == v) + v == __classPrivateFieldIn(v, _Foo_p1) < v; // Good precedence: (v == ((#p1 in v) < v)) + __classPrivateFieldIn(v, _Foo_p1) && __classPrivateFieldIn(v, _Foo_p1); // Good precedence: ((#p1 in v) && (#p1 in v)) } invalidLHS(v) { 'prop' in v; 10; - __classPrivateFieldIn(v, _p1); + __classPrivateFieldIn(v, _Foo_p1); 10; } } -_p1 = new WeakMap(); +_Foo_p1 = new WeakMap(); class Bar { constructor() { - _p1_1.set(this, 1); + _Bar_p1.set(this, 1); } check(v) { - __classPrivateFieldIn(v, _p1_1); // expect WeakMap '_p1_1' + __classPrivateFieldIn(v, _Bar_p1); // expect WeakMap '_Bar_p1' } } -_p1_1 = new WeakMap(); +_Bar_p1 = new WeakMap(); function syntaxError(v) { return in v; // expect `return in v` so runtime will have a syntax error } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.symbols b/tests/baselines/reference/privateNameInInExpressionTransform.symbols index 961cc80dd859f..b8d47b48a5354 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform.symbols @@ -11,7 +11,7 @@ class Foo { >check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 3, 12)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) - #p1 in v; // expect WeakMap '_p1' + #p1 in v; // expect WeakMap '_Foo_p1' >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) } precedence(v: any) { @@ -68,7 +68,7 @@ class Bar { >check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 29, 12)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10)) - #p1 in v; // expect WeakMap '_p1_1' + #p1 in v; // expect WeakMap '_Bar_p1' >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10)) } } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.types b/tests/baselines/reference/privateNameInInExpressionTransform.types index 553df1ce69058..cc21f7cec2ea1 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.types +++ b/tests/baselines/reference/privateNameInInExpressionTransform.types @@ -12,7 +12,7 @@ class Foo { >check : (v: any) => void >v : any - #p1 in v; // expect WeakMap '_p1' + #p1 in v; // expect WeakMap '_Foo_p1' >#p1 in v : boolean >v : any } @@ -91,7 +91,7 @@ class Bar { >check : (v: any) => void >v : any - #p1 in v; // expect WeakMap '_p1_1' + #p1 in v; // expect WeakMap '_Bar_p1' >#p1 in v : boolean >v : any } diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index bbef9a83230f5..0428fb3f09c1a 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -5,7 +5,7 @@ class Foo { #p1 = 1; check(v: any) { - #p1 in v; // expect WeakMap '_p1' + #p1 in v; // expect WeakMap '_Foo_p1' } precedence(v: any) { // '==' has lower precedence than 'in' @@ -31,7 +31,7 @@ class Foo { class Bar { #p1 = 1; check(v: any) { - #p1 in v; // expect WeakMap '_p1_1' + #p1 in v; // expect WeakMap '_Bar_p1' } } From dbd71138ca80e4a6c1e1e02ecc9a190c143d1ada Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 29 Mar 2021 13:32:49 +0100 Subject: [PATCH 22/29] update emit for static fields and methods Signed-off-by: Ashley Claymore --- src/compiler/factory/emitHelpers.ts | 14 +-- ...vateNameInInExpressionTransform.errors.txt | 53 ++++---- .../privateNameInInExpressionTransform.js | 71 ++++++----- ...privateNameInInExpressionTransform.symbols | 114 ++++++++++-------- .../privateNameInInExpressionTransform.types | 86 ++++++++----- .../privateNameInInExpressionTransform.ts | 31 +++-- 6 files changed, 210 insertions(+), 159 deletions(-) diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index eca0b8500100f..9d6595a024faf 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -34,7 +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(receiver: Expression, privateField: Identifier): Expression; + createClassPrivateFieldInHelper(receiver: Expression, state: Identifier): Expression; } export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory { @@ -394,10 +394,10 @@ namespace ts { return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, args); } - function createClassPrivateFieldInHelper(receiver: Expression, privateField: Identifier) { + function createClassPrivateFieldInHelper(receiver: Expression, state: Identifier) { // TODO(aclaymore): will need to change emit for static private fields context.requestEmitHelper(classPrivateFieldInHelper); - return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [receiver, privateField]); + return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [receiver, state]); } } @@ -966,11 +966,9 @@ namespace ts { importName: "__classPrivateFieldIn", scoped: false, text: ` - var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, privateMap) { - if (receiver === null || (typeof receiver !== 'object' && typeof receiver !== 'function')) { - throw new TypeError("Cannot use 'in' operator on non-object"); - } - return privateMap.has(receiver); + var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, state) { + 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); };` }; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt index 8f7b684463feb..0368b9ef750bc 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt @@ -1,60 +1,65 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(17,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,21): error TS1005: ';' expected. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(25,18): error TS1005: ';' expected. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(37,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +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(22,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +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(42,12): error TS18016: Private identifiers are not allowed outside class bodies. ==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (6 errors) ==== - // TODO(aclaymore) add cases for static fields - class Foo { - #p1 = 1; + #field = 1; + #method() {} + static #staticField= 2; + static #staticMethod() {} + check(v: any) { - #p1 in v; // expect WeakMap '_Foo_p1' + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor } precedence(v: any) { // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) - ~~~~~~~~~~~~~ + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + ~~~~~~~~~~~~~~~~ !!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. - ~~~~~~ + ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) - ~~~~~~~~ + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + ~~~~~~~~~~~ !!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) } invalidLHS(v: any) { 'prop' in v = 10; ~ !!! error TS1005: ';' expected. - #p1 in v = 10; - ~ + #field in v = 10; + ~ !!! error TS1005: ';' expected. } } class Bar { - #p1 = 1; + #field = 1; check(v: any) { - #p1 in v; // expect WeakMap '_Bar_p1' + #field in v; // expect Bar's 'field' WeakMap } } function syntaxError(v: Foo) { - return #p1 in v; // expect `return in v` so runtime will have a syntax error - ~~~ + return #field in v; // expect `return in v` so runtime will have a syntax error + ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.js b/tests/baselines/reference/privateNameInInExpressionTransform.js index cf299279d699f..cd7d3dbc61db2 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.js +++ b/tests/baselines/reference/privateNameInInExpressionTransform.js @@ -1,89 +1,96 @@ //// [privateNameInInExpressionTransform.ts] -// TODO(aclaymore) add cases for static fields - class Foo { - #p1 = 1; + #field = 1; + #method() {} + static #staticField= 2; + static #staticMethod() {} + check(v: any) { - #p1 in v; // expect WeakMap '_Foo_p1' + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor } precedence(v: any) { // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) } invalidLHS(v: any) { 'prop' in v = 10; - #p1 in v = 10; + #field in v = 10; } } class Bar { - #p1 = 1; + #field = 1; check(v: any) { - #p1 in v; // expect WeakMap '_Bar_p1' + #field in v; // expect Bar's 'field' WeakMap } } function syntaxError(v: Foo) { - return #p1 in v; // expect `return in v` so runtime will have a syntax error + return #field in v; // expect `return in v` so runtime will have a syntax error } export { } //// [privateNameInInExpressionTransform.js] -// TODO(aclaymore) add cases for static fields -var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, privateMap) { - if (receiver === null || (typeof receiver !== 'object' && typeof receiver !== 'function')) { - throw new TypeError("Cannot use 'in' operator on non-object"); - } - return privateMap.has(receiver); +var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, state) { + 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_p1, _Bar_p1; +var _Foo_instances, _a, _Foo_field, _Foo_method, _Foo_staticField, _Foo_staticMethod, _Bar_field; class Foo { constructor() { - _Foo_p1.set(this, 1); + _Foo_instances.add(this); + _Foo_field.set(this, 1); } check(v) { - __classPrivateFieldIn(v, _Foo_p1); // expect WeakMap '_Foo_p1' + __classPrivateFieldIn(v, _Foo_field); // expect Foo's 'field' WeakMap + __classPrivateFieldIn(v, _Foo_instances); // expect Foo's 'method' WeakSet + __classPrivateFieldIn(v, _a); // expect Foo's constructor + __classPrivateFieldIn(v, _a); // expect Foo's constructor } precedence(v) { // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == __classPrivateFieldIn(v, _Foo_p1) == v; // Good precedence: ((v == (#p1 in v)) == v) - v << __classPrivateFieldIn(v << v, _Foo_p1); // Good precedence: (v << (#p1 in (v << v))) - v << __classPrivateFieldIn(v, _Foo_p1) == v; // Good precedence: ((v << (#p1 in v)) == v) - v == __classPrivateFieldIn(v, _Foo_p1) < v; // Good precedence: (v == ((#p1 in v) < v)) - __classPrivateFieldIn(v, _Foo_p1) && __classPrivateFieldIn(v, _Foo_p1); // Good precedence: ((#p1 in v) && (#p1 in v)) + v == __classPrivateFieldIn(v, _Foo_field) == v; // Good precedence: ((v == (#field in v)) == v) + v << __classPrivateFieldIn(v << v, _Foo_field); // Good precedence: (v << (#field in (v << v))) + v << __classPrivateFieldIn(v, _Foo_field) == v; // Good precedence: ((v << (#field in v)) == v) + v == __classPrivateFieldIn(v, _Foo_field) < v; // Good precedence: (v == ((#field in v) < v)) + __classPrivateFieldIn(v, _Foo_field) && __classPrivateFieldIn(v, _Foo_field); // Good precedence: ((#field in v) && (#field in v)) } invalidLHS(v) { 'prop' in v; 10; - __classPrivateFieldIn(v, _Foo_p1); + __classPrivateFieldIn(v, _Foo_field); 10; } } -_Foo_p1 = new WeakMap(); +_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_p1.set(this, 1); + _Bar_field.set(this, 1); } check(v) { - __classPrivateFieldIn(v, _Bar_p1); // expect WeakMap '_Bar_p1' + __classPrivateFieldIn(v, _Bar_field); // expect Bar's 'field' WeakMap } } -_Bar_p1 = new WeakMap(); +_Bar_field = new WeakMap(); function syntaxError(v) { return in v; // expect `return in v` so runtime will have a syntax error } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.symbols b/tests/baselines/reference/privateNameInInExpressionTransform.symbols index b8d47b48a5354..c0cc9938cd8d2 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform.symbols @@ -1,85 +1,101 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === -// TODO(aclaymore) add cases for static fields - class Foo { >Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) - #p1 = 1; ->#p1 : Symbol(Foo.#p1, Decl(privateNameInInExpressionTransform.ts, 2, 11)) + #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, 3, 12)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) +>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 +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #method in v; // expect Foo's 'method' WeakSet +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #staticField in v; // expect Foo's constructor +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) - #p1 in v; // expect WeakMap '_Foo_p1' ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10)) + #staticMethod in v; // expect Foo's constructor +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) } precedence(v: any) { ->precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 6, 5)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) +>precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 11, 5)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) - - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) - - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) - - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) - - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15)) + v == #field in v == v; // Good precedence: ((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: (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: ((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: (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)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) } invalidLHS(v: any) { ->invalidLHS : Symbol(Foo.invalidLHS, Decl(privateNameInInExpressionTransform.ts, 21, 5)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15)) +>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, 22, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) - #p1 in v = 10; ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15)) + #field in v = 10; +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) } } class Bar { ->Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 26, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 31, 1)) - #p1 = 1; ->#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 28, 11)) + #field = 1; +>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11)) check(v: any) { ->check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 29, 12)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10)) +>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 34, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) - #p1 in v; // expect WeakMap '_Bar_p1' ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10)) + #field in v; // expect Bar's 'field' WeakMap +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) } } function syntaxError(v: Foo) { ->syntaxError : Symbol(syntaxError, Decl(privateNameInInExpressionTransform.ts, 33, 1)) ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 21)) +>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 #p1 in v; // expect `return in v` so runtime will have a syntax error ->v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 21)) + return #field in v; // expect `return in v` so runtime will have a syntax error +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.types b/tests/baselines/reference/privateNameInInExpressionTransform.types index cc21f7cec2ea1..5c68549be8fd4 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform.types +++ b/tests/baselines/reference/privateNameInInExpressionTransform.types @@ -1,19 +1,39 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === -// TODO(aclaymore) add cases for static fields - class Foo { >Foo : Foo - #p1 = 1; ->#p1 : number + #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 - #p1 in v; // expect WeakMap '_Foo_p1' ->#p1 in v : boolean + #field in v; // expect Foo's 'field' WeakMap +>#field in v : boolean +>v : any + + #method in v; // expect Foo's 'method' WeakSet +>#method in v : boolean +>v : any + + #staticField in v; // expect Foo's constructor +>#staticField in v : boolean +>v : any + + #staticMethod in v; // expect Foo's constructor +>#staticMethod in v : boolean >v : any } precedence(v: any) { @@ -24,43 +44,43 @@ class Foo { // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) ->v == #p1 in v == v : boolean ->v == #p1 in v : boolean + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) +>v == #field in v == v : boolean +>v == #field in v : boolean >v : any ->#p1 in v : boolean +>#field in v : boolean >v : any >v : any - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) ->v << #p1 in v << v : number + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) +>v << #field in v << v : number >v : any ->#p1 in v << v : boolean +>#field in v << v : boolean >v << v : number >v : any >v : any - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) ->v << #p1 in v == v : boolean ->v << #p1 in v : number + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) +>v << #field in v == v : boolean +>v << #field in v : number >v : any ->#p1 in v : boolean +>#field in v : boolean >v : any >v : any - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) ->v == #p1 in v < v : boolean + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) +>v == #field in v < v : boolean >v : any ->#p1 in v < v : boolean ->#p1 in v : boolean +>#field in v < v : boolean +>#field in v : boolean >v : any >v : any - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) ->#p1 in v && #p1 in v : boolean ->#p1 in v : boolean + #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 >v : any ->#p1 in v : boolean +>#field in v : boolean >v : Foo } invalidLHS(v: any) { @@ -73,8 +93,8 @@ class Foo { >v : any >10 : 10 - #p1 in v = 10; ->#p1 in v : boolean + #field in v = 10; +>#field in v : boolean >v : any >10 : 10 } @@ -83,16 +103,16 @@ class Foo { class Bar { >Bar : Bar - #p1 = 1; ->#p1 : number + #field = 1; +>#field : number >1 : 1 check(v: any) { >check : (v: any) => void >v : any - #p1 in v; // expect WeakMap '_Bar_p1' ->#p1 in v : boolean + #field in v; // expect Bar's 'field' WeakMap +>#field in v : boolean >v : any } } @@ -101,8 +121,8 @@ function syntaxError(v: Foo) { >syntaxError : (v: Foo) => any >v : Foo - return #p1 in v; // expect `return in v` so runtime will have a syntax error ->#p1 in v : any + return #field in v; // expect `return in v` so runtime will have a syntax error +>#field in v : any >v : Foo } diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index 0428fb3f09c1a..2518012b3e1b0 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -1,42 +1,47 @@ // @target: es2020 -// TODO(aclaymore) add cases for static fields - class Foo { - #p1 = 1; + #field = 1; + #method() {} + static #staticField= 2; + static #staticMethod() {} + check(v: any) { - #p1 in v; // expect WeakMap '_Foo_p1' + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor } precedence(v: any) { // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #p1 in v == v; // Good precedence: ((v == (#p1 in v)) == v) + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) - v << #p1 in v << v; // Good precedence: (v << (#p1 in (v << v))) + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) - v << #p1 in v == v; // Good precedence: ((v << (#p1 in v)) == v) + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) - v == #p1 in v < v; // Good precedence: (v == ((#p1 in v) < v)) + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) - #p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v)) + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) } invalidLHS(v: any) { 'prop' in v = 10; - #p1 in v = 10; + #field in v = 10; } } class Bar { - #p1 = 1; + #field = 1; check(v: any) { - #p1 in v; // expect WeakMap '_Bar_p1' + #field in v; // expect Bar's 'field' WeakMap } } function syntaxError(v: Foo) { - return #p1 in v; // expect `return in v` so runtime will have a syntax error + return #field in v; // expect `return in v` so runtime will have a syntax error } export { } From 91cfecbb7224383f745e1a3cae592b748bd0835c Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 29 Mar 2021 15:18:50 +0100 Subject: [PATCH 23/29] narrow to class for static field Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 22 +- .../privateNameInInExpression.errors.txt | 108 +++++---- .../reference/privateNameInInExpression.js | 120 ++++++---- .../privateNameInInExpression.symbols | 224 ++++++++++-------- .../reference/privateNameInInExpression.types | 130 ++++++---- .../privateNames/privateNameInInExpression.ts | 61 +++-- 6 files changed, 399 insertions(+), 266 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 86f3eb671b08e..0429bba320f0d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23238,8 +23238,6 @@ namespace ts { return type; } - // TODO(aclaymore): add logic for private static fields - const privateId = expr.name; const symbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); if (symbol === undefined) { @@ -23247,11 +23245,21 @@ namespace ts { } const classSymbol = symbol.parent!; const classType = getTypeOfSymbol(classSymbol); - const ctorSigs = getSignaturesOfType(classType, SignatureKind.Construct); - // TODO(aclaymore): verify assertion is valid - Debug.assert(ctorSigs.length > 0, "narrowTypeByPrivateIdentifierInInExpression should always find the class signature"); - const instanceType = getReturnTypeOfSignature(ctorSigs[0]); - return getNarrowedType(type, instanceType, assumeTrue, isTypeDerivedFrom); + // TODO(aclaymore): clean up finding out if the identifier is 'static' + const decleration = symbol.declarations?.[0]; + Debug.assert(decleration, "narrowTypeByPrivateIdentifierInInExpression should always get the field declaration"); + const isStatic = hasSyntacticModifier(decleration, ModifierFlags.Static); + let targetType: Type; + if (isStatic) { + targetType = classType + } + else { + const ctorSigs = getSignaturesOfType(classType, SignatureKind.Construct); + // TODO(aclaymore): verify assertion is valid + Debug.assert(ctorSigs.length > 0, "narrowTypeByPrivateIdentifierInInExpression should always find the class signature"); + targetType = getReturnTypeOfSignature(ctorSigs[0]); + } + return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); } function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 73cf465886c92..c4f1bc88d4d67 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,72 +1,78 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(16,26): error TS2571: Object is of type 'unknown'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(18,19): error TS2304: Cannot find name '#p2'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,20): error TS2304: Cannot find name '#p1'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,20): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,14): error TS2304: Cannot find name '#p1'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,14): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,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(42,24): error TS2531: Object is possibly 'null'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(95,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(5,27): 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/privateNameInInExpression.ts(20,29): error TS2571: Object is of type 'unknown'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,19): error TS2304: Cannot find name '#typo'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,20): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,20): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(26,14): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(26,14): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(28,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(45,27): error TS2531: Object is possibly 'null'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(110,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (10 errors) ==== // TODO(aclaymore) split up into separate cases class Foo { - #p1 = 1; + #field = 1; + 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. + #method() {} + static #staticMethod() {} + basics(v: any) { - const a = #p1 in v; // Good - a is boolean + const a = #field in v; // Good - a is boolean - const b = #p1 in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; // Good - b is boolean - const c = #p1 in (v as {}); // Good - c is boolean + const c = #field in (v as {}); // Good - c is boolean - const d = #p1 in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); // Good d is boolean (not true) - const e = #p1 in (v as never); // Good e is boolean + const e = #field in (v as never); // Good e is boolean - const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any - ~~~~~~~~~~~~~~ + const f = #field in (v as unknown); // Bad - RHS of in must be object type or any + ~~~~~~~~~~~~~~ !!! error TS2571: Object is of type 'unknown'. - const g = #p2 in v; // Bad - Invalid privateID - ~~~~~~~~ -!!! error TS2304: Cannot find name '#p2'. + const g = #typo in v; // Bad - Invalid privateID + ~~~~~~~~~~ +!!! error TS2304: Cannot find name '#typo'. - const h = (#p1) in v; // Bad - private id is not an expression on its own - ~~~ -!!! error TS2304: Cannot find name '#p1'. - ~~~ + const h = (#field) in v; // Bad - private id is not an expression on its own + ~~~~~~ +!!! error TS2304: Cannot find name '#field'. + ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. - for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed - ~~~ -!!! error TS2304: Cannot find name '#p1'. - ~~~ + for (#field in v) { /* no-op */ } // Bad - 'in' not allowed + ~~~~~~ +!!! error TS2304: Cannot find name '#field'. + ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any - ~~~~~~~~ + for (let x in #field in v) { /* no-op */ } // 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'. - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid } whitespace(v: any) { - const a = v && /*0*/#p1/*1*/ + 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) { if (typeof u === 'object') { - - if (#p1 in n) { + if (#field in n) { n; // good n is never } - if (#p1 in u) { - ~ + if (#field in u) { + ~ !!! error TS2531: Object is possibly 'null'. u; // good u is Foo } else { @@ -74,33 +80,45 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t } if (u !== null) { - if (#p1 in u) { + 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 (#p1 in fb) { + if (#field in fb) { fb; // good fb is Foo } else { fb; // good fb is Bar } - if (#p1 in fs) { + if (#field in fs) { fs; // good fs is FooSub } else { fs; // good fs is never } - if (#p1 in b) { + if (#field in b) { b; // good b is 'Bar & Foo' } else { b; // good b is Bar } - if (#p1 in fsb) { + if (#field in fsb) { fsb; // good fsb is FooSub } else { fsb; // good fsb is Bar @@ -108,7 +126,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t class Nested { m(v: any) { - if (#p1 in v) { + if (#field in v) { v; // good v is Foo } } @@ -120,8 +138,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t class Bar { notFoo = true } function error(v: Foo) { - return #p1 in v; // Bad - outside of class - ~~~ + return #field in v; // Bad - outside of class + ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index b9f4ed20bc49a..4a87d5c1fb390 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -2,78 +2,93 @@ // TODO(aclaymore) split up into separate cases class Foo { - #p1 = 1; + #field = 1; + static #staticField = 2; + #method() {} + static #staticMethod() {} + basics(v: any) { - const a = #p1 in v; // Good - a is boolean + const a = #field in v; // Good - a is boolean - const b = #p1 in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; // Good - b is boolean - const c = #p1 in (v as {}); // Good - c is boolean + const c = #field in (v as {}); // Good - c is boolean - const d = #p1 in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); // Good d is boolean (not true) - const e = #p1 in (v as never); // Good e is boolean + const e = #field in (v as never); // Good e is boolean - const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const f = #field in (v as unknown); // Bad - RHS of in must be object type or any - const g = #p2 in v; // Bad - Invalid privateID + const g = #typo in v; // Bad - Invalid privateID - const h = (#p1) in v; // Bad - private id is not an expression on its own + const h = (#field) in v; // Bad - private id is not an expression on its own - for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed + for (#field in v) { /* no-op */ } // Bad - 'in' not allowed - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid } whitespace(v: any) { - const a = v && /*0*/#p1/*1*/ + 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) { if (typeof u === 'object') { - - if (#p1 in n) { + if (#field in n) { n; // good n is never } - if (#p1 in u) { + if (#field in u) { u; // good u is Foo } else { u; // good u is object | null } if (u !== null) { - if (#p1 in u) { + 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 (#p1 in fb) { + if (#field in fb) { fb; // good fb is Foo } else { fb; // good fb is Bar } - if (#p1 in fs) { + if (#field in fs) { fs; // good fs is FooSub } else { fs; // good fs is never } - if (#p1 in b) { + if (#field in b) { b; // good b is 'Bar & Foo' } else { b; // good b is Bar } - if (#p1 in fsb) { + if (#field in fsb) { fsb; // good fsb is FooSub } else { fsb; // good fsb is Bar @@ -81,7 +96,7 @@ class Foo { class Nested { m(v: any) { - if (#p1 in v) { + if (#field in v) { v; // good v is Foo } } @@ -93,7 +108,7 @@ class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } function error(v: Foo) { - return #p1 in v; // Bad - outside of class + return #field in v; // Bad - outside of class } export { } @@ -103,66 +118,78 @@ export { } // TODO(aclaymore) split up into separate cases class Foo { constructor() { - this.#p1 = 1; + this.#field = 1; } - #p1; + #field; + static #staticField; + #method() { } + static #staticMethod() { } basics(v) { - const a = #p1 in v; // Good - a is boolean - const b = #p1 in v.p1.p2; // Good - b is boolean - const c = #p1 in v; // Good - c is boolean - const d = #p1 in v; // Good d is boolean (not true) - const e = #p1 in v; // Good e is boolean - const f = #p1 in v; // Bad - RHS of in must be object type or any - const g = #p2 in v; // Bad - Invalid privateID - const h = (#p1) in v; // Bad - private id is not an expression on its own - for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any - for (let x in #p1 in v) { /* no-op */ } // Good - weird but valid + const a = #field in v; // Good - a is boolean + const b = #field in v.p1.p2; // Good - b is boolean + const c = #field in v; // Good - c is boolean + const d = #field in v; // Good d is boolean (not true) + const e = #field in v; // Good e is boolean + const f = #field in v; // Bad - RHS of in must be object type or any + const g = #typo in v; // Bad - Invalid privateID + const h = (#field) in v; // Bad - private id is not an expression on its own + for (#field in v) { /* no-op */ } // Bad - 'in' not allowed + for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any + for (let x in #field in v) { /* no-op */ } // Good - weird but valid } whitespace(v) { - const a = v && /*0*/ #p1/*1*/ + const a = v && /*0*/ #field/*1*/ /*2*/ in /*3*/ /*4*/ v; /*5*/ } flow(u, n, fb, fs, b, fsb) { if (typeof u === 'object') { - if (#p1 in n) { + if (#field in n) { n; // good n is never } - if (#p1 in u) { + if (#field in u) { u; // good u is Foo } else { u; // good u is object | null } if (u !== null) { - if (#p1 in u) { + 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 (#p1 in fb) { + if (#field in fb) { fb; // good fb is Foo } else { fb; // good fb is Bar } - if (#p1 in fs) { + if (#field in fs) { fs; // good fs is FooSub } else { fs; // good fs is never } - if (#p1 in b) { + if (#field in b) { b; // good b is 'Bar & Foo' } else { b; // good b is Bar } - if (#p1 in fsb) { + if (#field in fsb) { fsb; // good fsb is FooSub } else { @@ -170,13 +197,14 @@ class Foo { } class Nested { m(v) { - if (#p1 in v) { + if (#field in v) { v; // good v is Foo } } } } } +Foo.#staticField = 2; class FooSub extends Foo { constructor() { super(...arguments); @@ -189,6 +217,6 @@ class Bar { } } function error(v) { - return #p1 in v; // Bad - outside of class + return #field in v; // Bad - outside of class } export {}; diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index f79c86b1135df..738d1d4163314 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -4,178 +4,208 @@ class Foo { >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) - #p1 = 1; ->#p1 : Symbol(Foo.#p1, Decl(privateNameInInExpression.ts, 2, 11)) + #field = 1; +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 2, 11)) + + static #staticField = 2; +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpression.ts, 3, 15)) + + #method() {} +>#method : Symbol(Foo.#method, Decl(privateNameInInExpression.ts, 4, 28)) + + static #staticMethod() {} +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 5, 16)) basics(v: any) { ->basics : Symbol(Foo.basics, Decl(privateNameInInExpression.ts, 3, 12)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) +>basics : Symbol(Foo.basics, Decl(privateNameInInExpression.ts, 6, 29)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - const a = #p1 in v; // Good - a is boolean ->a : Symbol(a, Decl(privateNameInInExpression.ts, 5, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const a = #field in v; // Good - a is boolean +>a : Symbol(a, Decl(privateNameInInExpression.ts, 9, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - const b = #p1 in v.p1.p2; // Good - b is boolean ->b : Symbol(b, Decl(privateNameInInExpression.ts, 7, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const b = #field in v.p1.p2; // Good - b is boolean +>b : Symbol(b, Decl(privateNameInInExpression.ts, 11, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - const c = #p1 in (v as {}); // Good - c is boolean ->c : Symbol(c, Decl(privateNameInInExpression.ts, 9, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const c = #field in (v as {}); // Good - c is boolean +>c : Symbol(c, Decl(privateNameInInExpression.ts, 13, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - const d = #p1 in (v as Foo); // Good d is boolean (not true) ->d : Symbol(d, Decl(privateNameInInExpression.ts, 11, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const d = #field in (v as Foo); // Good d is boolean (not true) +>d : Symbol(d, Decl(privateNameInInExpression.ts, 15, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) - const e = #p1 in (v as never); // Good e is boolean ->e : Symbol(e, Decl(privateNameInInExpression.ts, 13, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const e = #field in (v as never); // Good e is boolean +>e : Symbol(e, Decl(privateNameInInExpression.ts, 17, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any ->f : Symbol(f, Decl(privateNameInInExpression.ts, 15, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const f = #field in (v as unknown); // Bad - RHS of in must be object type or any +>f : Symbol(f, Decl(privateNameInInExpression.ts, 19, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - const g = #p2 in v; // Bad - Invalid privateID ->g : Symbol(g, Decl(privateNameInInExpression.ts, 17, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const g = #typo in v; // Bad - Invalid privateID +>g : Symbol(g, Decl(privateNameInInExpression.ts, 21, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - const h = (#p1) in v; // Bad - private id is not an expression on its own ->h : Symbol(h, Decl(privateNameInInExpression.ts, 19, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + const h = (#field) in v; // Bad - private id is not an expression on its own +>h : Symbol(h, Decl(privateNameInInExpression.ts, 23, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + for (#field in v) { /* no-op */ } // Bad - 'in' not allowed +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any ->x : Symbol(x, Decl(privateNameInInExpression.ts, 23, 16)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any +>x : Symbol(x, Decl(privateNameInInExpression.ts, 27, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid ->x : Symbol(x, Decl(privateNameInInExpression.ts, 25, 16)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 4, 11)) + for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid +>x : Symbol(x, Decl(privateNameInInExpression.ts, 29, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) } whitespace(v: any) { ->whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 27, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 15)) +>whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 31, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 32, 15)) - const a = v && /*0*/#p1/*1*/ ->a : Symbol(a, Decl(privateNameInInExpression.ts, 29, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 15)) + const a = v && /*0*/#field/*1*/ +>a : Symbol(a, Decl(privateNameInInExpression.ts, 33, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 32, 15)) /*2*/in/*3*/ /*4*/v/*5*/ ->v : Symbol(v, Decl(privateNameInInExpression.ts, 28, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 32, 15)) } flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { ->flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 32, 5)) ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) ->n : Symbol(n, Decl(privateNameInInExpression.ts, 33, 20)) ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) +>flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 36, 5)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>n : Symbol(n, Decl(privateNameInInExpression.ts, 37, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) ->b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 105, 48)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 103, 1)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 105, 48)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 103, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 105, 48)) if (typeof u === 'object') { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) - if (#p1 in n) { ->n : Symbol(n, Decl(privateNameInInExpression.ts, 33, 20)) + if (#field in n) { +>n : Symbol(n, Decl(privateNameInInExpression.ts, 37, 20)) n; // good n is never ->n : Symbol(n, Decl(privateNameInInExpression.ts, 33, 20)) +>n : Symbol(n, Decl(privateNameInInExpression.ts, 37, 20)) } - if (#p1 in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) + if (#field in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) } else { u; // good u is object | null ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) } if (u !== null) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) - if (#p1 in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) + if (#field in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) } else { u; // good u is object ->u : Symbol(u, Decl(privateNameInInExpression.ts, 33, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) + } + + if (#method in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) + + u; // good u is Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) + } + + if (#staticField in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) + + u; // good u is typeof Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) + } + + if (#staticMethod in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) + + u; // good u is typeof Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) } } } - if (#p1 in fb) { ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) + if (#field in fb) { +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) fb; // good fb is Foo ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) } else { fb; // good fb is Bar ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 33, 30)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) } - if (#p1 in fs) { ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) + if (#field in fs) { +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) fs; // good fs is FooSub ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) } else { fs; // good fs is never ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 33, 45)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) } - if (#p1 in b) { ->b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) + if (#field in b) { +>b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) b; // good b is 'Bar & Foo' ->b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) } else { b; // good b is Bar ->b : Symbol(b, Decl(privateNameInInExpression.ts, 33, 57)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) } - if (#p1 in fsb) { ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) + if (#field in fsb) { +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) fsb; // good fsb is FooSub ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) } else { fsb; // good fsb is Bar ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 33, 65)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) } class Nested { ->Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 78, 9)) +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 93, 9)) m(v: any) { ->m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 80, 22)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 81, 14)) +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 95, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 96, 14)) - if (#p1 in v) { ->v : Symbol(v, Decl(privateNameInInExpression.ts, 81, 14)) + if (#field in v) { +>v : Symbol(v, Decl(privateNameInInExpression.ts, 96, 14)) v; // good v is Foo ->v : Symbol(v, Decl(privateNameInInExpression.ts, 81, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 96, 14)) } } } @@ -183,21 +213,21 @@ class Foo { } class FooSub extends Foo { subTypeOfFoo = true } ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 88, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 103, 1)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 90, 26)) +>subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 105, 26)) class Bar { notFoo = true } ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 90, 48)) ->notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 91, 11)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 105, 48)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 106, 11)) function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpression.ts, 91, 27)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 93, 15)) +>error : Symbol(error, Decl(privateNameInInExpression.ts, 106, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 108, 15)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) - return #p1 in v; // Bad - outside of class ->v : Symbol(v, Decl(privateNameInInExpression.ts, 93, 15)) + return #field in v; // Bad - outside of class +>v : Symbol(v, Decl(privateNameInInExpression.ts, 108, 15)) } export { } diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 6221fa88a89e6..570e93014918d 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -4,81 +4,91 @@ class Foo { >Foo : Foo - #p1 = 1; ->#p1 : number + #field = 1; +>#field : number >1 : 1 + static #staticField = 2; +>#staticField : number +>2 : 2 + + #method() {} +>#method : () => void + + static #staticMethod() {} +>#staticMethod : () => void + basics(v: any) { >basics : (v: any) => void >v : any - const a = #p1 in v; // Good - a is boolean + const a = #field in v; // Good - a is boolean >a : boolean ->#p1 in v : boolean +>#field in v : boolean >v : any - const b = #p1 in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; // Good - b is boolean >b : boolean ->#p1 in v.p1.p2 : boolean +>#field in v.p1.p2 : boolean >v.p1.p2 : any >v.p1 : any >v : any >p1 : any >p2 : any - const c = #p1 in (v as {}); // Good - c is boolean + const c = #field in (v as {}); // Good - c is boolean >c : boolean ->#p1 in (v as {}) : boolean +>#field in (v as {}) : boolean >(v as {}) : {} >v as {} : {} >v : any - const d = #p1 in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); // Good d is boolean (not true) >d : boolean ->#p1 in (v as Foo) : boolean +>#field in (v as Foo) : boolean >(v as Foo) : Foo >v as Foo : Foo >v : any - const e = #p1 in (v as never); // Good e is boolean + const e = #field in (v as never); // Good e is boolean >e : boolean ->#p1 in (v as never) : boolean +>#field in (v as never) : boolean >(v as never) : never >v as never : never >v : any - const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const f = #field in (v as unknown); // Bad - RHS of in must be object type or any >f : boolean ->#p1 in (v as unknown) : boolean +>#field in (v as unknown) : boolean >(v as unknown) : unknown >v as unknown : unknown >v : any - const g = #p2 in v; // Bad - Invalid privateID + const g = #typo in v; // Bad - Invalid privateID >g : any ->#p2 in v : any +>#typo in v : any >v : any - const h = (#p1) in v; // Bad - private id is not an expression on its own + const h = (#field) in v; // Bad - private id is not an expression on its own >h : boolean ->(#p1) in v : boolean ->(#p1) : any ->#p1 : any +>(#field) in v : boolean +>(#field) : any +>#field : any >v : any - for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed ->#p1 : any + for (#field in v) { /* no-op */ } // Bad - 'in' not allowed +>#field : any >v : any - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any >x : string ->#p1 in v : boolean +>#field in v : boolean >v : any - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid >x : string ->#p1 in v as any : any ->#p1 in v : boolean +>#field in v as any : any +>#field in v : boolean >v : any } @@ -86,11 +96,11 @@ class Foo { >whitespace : (v: any) => void >v : any - const a = v && /*0*/#p1/*1*/ + const a = v && /*0*/#field/*1*/ >a : any ->v && /*0*/#p1/*1*/ /*2*/in/*3*/ /*4*/v : any +>v && /*0*/#field/*1*/ /*2*/in/*3*/ /*4*/v : any >v : any ->#p1/*1*/ /*2*/in/*3*/ /*4*/v : boolean +>#field/*1*/ /*2*/in/*3*/ /*4*/v : boolean /*2*/in/*3*/ /*4*/v/*5*/ @@ -111,16 +121,16 @@ class Foo { >u : unknown >'object' : "object" - if (#p1 in n) { ->#p1 in n : boolean + if (#field in n) { +>#field in n : boolean >n : never n; // good n is never >n : never } - if (#p1 in u) { ->#p1 in u : boolean + if (#field in u) { +>#field in u : boolean >u : object | null u; // good u is Foo @@ -136,8 +146,8 @@ class Foo { >u : object | null >null : null - if (#p1 in u) { ->#p1 in u : boolean + if (#field in u) { +>#field in u : boolean >u : object u; // good u is Foo @@ -147,11 +157,35 @@ class Foo { u; // good u is object >u : object } + + if (#method in u) { +>#method in u : boolean +>u : object + + u; // good u is Foo +>u : Foo + } + + if (#staticField in u) { +>#staticField in u : boolean +>u : object + + u; // good u is typeof Foo +>u : typeof Foo + } + + if (#staticMethod in u) { +>#staticMethod in u : boolean +>u : object + + u; // good u is typeof Foo +>u : typeof Foo + } } } - if (#p1 in fb) { ->#p1 in fb : boolean + if (#field in fb) { +>#field in fb : boolean >fb : Foo | Bar fb; // good fb is Foo @@ -162,8 +196,8 @@ class Foo { >fb : Bar } - if (#p1 in fs) { ->#p1 in fs : boolean + if (#field in fs) { +>#field in fs : boolean >fs : FooSub fs; // good fs is FooSub @@ -174,8 +208,8 @@ class Foo { >fs : never } - if (#p1 in b) { ->#p1 in b : boolean + if (#field in b) { +>#field in b : boolean >b : Bar b; // good b is 'Bar & Foo' @@ -186,8 +220,8 @@ class Foo { >b : Bar } - if (#p1 in fsb) { ->#p1 in fsb : boolean + if (#field in fsb) { +>#field in fsb : boolean >fsb : Bar | FooSub fsb; // good fsb is FooSub @@ -205,8 +239,8 @@ class Foo { >m : (v: any) => void >v : any - if (#p1 in v) { ->#p1 in v : boolean + if (#field in v) { +>#field in v : boolean >v : any v; // good v is Foo @@ -232,8 +266,8 @@ function error(v: Foo) { >error : (v: Foo) => any >v : Foo - return #p1 in v; // Bad - outside of class ->#p1 in v : any + return #field in v; // Bad - outside of class +>#field in v : any >v : Foo } diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index dc70f6c260429..037ac9eb08b53 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -4,78 +4,93 @@ // TODO(aclaymore) split up into separate cases class Foo { - #p1 = 1; + #field = 1; + static #staticField = 2; + #method() {} + static #staticMethod() {} + basics(v: any) { - const a = #p1 in v; // Good - a is boolean + const a = #field in v; // Good - a is boolean - const b = #p1 in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; // Good - b is boolean - const c = #p1 in (v as {}); // Good - c is boolean + const c = #field in (v as {}); // Good - c is boolean - const d = #p1 in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); // Good d is boolean (not true) - const e = #p1 in (v as never); // Good e is boolean + const e = #field in (v as never); // Good e is boolean - const f = #p1 in (v as unknown); // Bad - RHS of in must be object type or any + const f = #field in (v as unknown); // Bad - RHS of in must be object type or any - const g = #p2 in v; // Bad - Invalid privateID + const g = #typo in v; // Bad - Invalid privateID - const h = (#p1) in v; // Bad - private id is not an expression on its own + const h = (#field) in v; // Bad - private id is not an expression on its own - for (#p1 in v) { /* no-op */ } // Bad - 'in' not allowed + for (#field in v) { /* no-op */ } // Bad - 'in' not allowed - for (let x in #p1 in v) { /* no-op */ } // Bad - rhs of in should be a object/any + for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any - for (let x in #p1 in v as any) { /* no-op */ } // Good - weird but valid + for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid } whitespace(v: any) { - const a = v && /*0*/#p1/*1*/ + 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) { if (typeof u === 'object') { - - if (#p1 in n) { + if (#field in n) { n; // good n is never } - if (#p1 in u) { + if (#field in u) { u; // good u is Foo } else { u; // good u is object | null } if (u !== null) { - if (#p1 in u) { + 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 (#p1 in fb) { + if (#field in fb) { fb; // good fb is Foo } else { fb; // good fb is Bar } - if (#p1 in fs) { + if (#field in fs) { fs; // good fs is FooSub } else { fs; // good fs is never } - if (#p1 in b) { + if (#field in b) { b; // good b is 'Bar & Foo' } else { b; // good b is Bar } - if (#p1 in fsb) { + if (#field in fsb) { fsb; // good fsb is FooSub } else { fsb; // good fsb is Bar @@ -83,7 +98,7 @@ class Foo { class Nested { m(v: any) { - if (#p1 in v) { + if (#field in v) { v; // good v is Foo } } @@ -95,7 +110,7 @@ class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } function error(v: Foo) { - return #p1 in v; // Bad - outside of class + return #field in v; // Bad - outside of class } export { } From 5eead91d4a0ba6f96107cc9253089524939421ca Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 29 Mar 2021 17:19:13 +0100 Subject: [PATCH 24/29] tidy up TODOs Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 13 +- src/compiler/factory/emitHelpers.ts | 1 - src/compiler/parser.ts | 1 - .../privateNameInInExpression.errors.txt | 26 ++- .../reference/privateNameInInExpression.js | 11 +- .../privateNameInInExpression.symbols | 174 +++++++++--------- .../reference/privateNameInInExpression.types | 8 +- .../privateNames/privateNameInInExpression.ts | 6 +- 8 files changed, 107 insertions(+), 133 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0429bba320f0d..516f77ca2f5f1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23245,18 +23245,15 @@ namespace ts { } const classSymbol = symbol.parent!; const classType = getTypeOfSymbol(classSymbol); - // TODO(aclaymore): clean up finding out if the identifier is 'static' - const decleration = symbol.declarations?.[0]; - Debug.assert(decleration, "narrowTypeByPrivateIdentifierInInExpression should always get the field declaration"); - const isStatic = hasSyntacticModifier(decleration, ModifierFlags.Static); + const firstDecl = symbol.declarations?.[0]; + Debug.assert(firstDecl, "should always have a declaration"); let targetType: Type; - if (isStatic) { - targetType = classType + if (hasSyntacticModifier(firstDecl, ModifierFlags.Static)) { + targetType = classType; } else { const ctorSigs = getSignaturesOfType(classType, SignatureKind.Construct); - // TODO(aclaymore): verify assertion is valid - Debug.assert(ctorSigs.length > 0, "narrowTypeByPrivateIdentifierInInExpression should always find the class signature"); + Debug.assert(ctorSigs.length > 0, "should always have a constructor"); targetType = getReturnTypeOfSignature(ctorSigs[0]); } return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 9d6595a024faf..9df1d4126cd43 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -395,7 +395,6 @@ namespace ts { } function createClassPrivateFieldInHelper(receiver: Expression, state: Identifier) { - // TODO(aclaymore): will need to change emit for static private fields context.requestEmitHelper(classPrivateFieldInHelper); return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [receiver, state]); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 82c3e777baf3d..c9af6ab7e4527 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4441,7 +4441,6 @@ namespace ts { Debug.assert(inDisallowInContext() === false, "parsePrivateIdentifierInInExpression should only have been called if 'in' is allowed"); const id = parsePrivateIdentifier(); if (token() !== SyntaxKind.InKeyword) { - // TODO(aclaymore) use better error return createMissingNode(SyntaxKind.InKeyword, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, tokenToString(SyntaxKind.InKeyword)); } diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index c4f1bc88d4d67..5460b9288cfa2 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,18 +1,16 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(5,27): 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/privateNameInInExpression.ts(20,29): error TS2571: Object is of type 'unknown'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,19): error TS2304: Cannot find name '#typo'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,20): error TS2304: Cannot find name '#field'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,20): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(26,14): error TS2304: Cannot find name '#field'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(26,14): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(28,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(45,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(110,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(3,27): 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/privateNameInInExpression.ts(18,29): error TS2571: Object is of type 'unknown'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,19): error TS2304: Cannot find name '#typo'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,20): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,20): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,14): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,14): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(26,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(108,12): error TS18016: Private identifiers are not allowed outside class bodies. ==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (10 errors) ==== - // TODO(aclaymore) split up into separate cases - class Foo { #field = 1; static #staticField = 2; @@ -137,11 +135,9 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } - function error(v: Foo) { + function badSyntax(v: Foo) { return #field in v; // Bad - outside of class ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } - - export { } \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 4a87d5c1fb390..24fb7c110a4c2 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -1,6 +1,4 @@ //// [privateNameInInExpression.ts] -// TODO(aclaymore) split up into separate cases - class Foo { #field = 1; static #staticField = 2; @@ -107,15 +105,13 @@ class Foo { class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } -function error(v: Foo) { +function badSyntax(v: Foo) { return #field in v; // Bad - outside of class } - -export { } //// [privateNameInInExpression.js] -// TODO(aclaymore) split up into separate cases +"use strict"; class Foo { constructor() { this.#field = 1; @@ -216,7 +212,6 @@ class Bar { this.notFoo = true; } } -function error(v) { +function badSyntax(v) { return #field in v; // Bad - outside of class } -export {}; diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 738d1d4163314..f979d3c1bf5e5 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -1,211 +1,209 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === -// TODO(aclaymore) split up into separate cases - class Foo { >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) #field = 1; ->#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 2, 11)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) static #staticField = 2; ->#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpression.ts, 3, 15)) +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpression.ts, 1, 15)) #method() {} ->#method : Symbol(Foo.#method, Decl(privateNameInInExpression.ts, 4, 28)) +>#method : Symbol(Foo.#method, Decl(privateNameInInExpression.ts, 2, 28)) static #staticMethod() {} ->#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 5, 16)) +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 3, 16)) basics(v: any) { ->basics : Symbol(Foo.basics, Decl(privateNameInInExpression.ts, 6, 29)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>basics : Symbol(Foo.basics, Decl(privateNameInInExpression.ts, 4, 29)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) const a = #field in v; // Good - a is boolean ->a : Symbol(a, Decl(privateNameInInExpression.ts, 9, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>a : Symbol(a, Decl(privateNameInInExpression.ts, 7, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) const b = #field in v.p1.p2; // Good - b is boolean ->b : Symbol(b, Decl(privateNameInInExpression.ts, 11, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 9, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) const c = #field in (v as {}); // Good - c is boolean ->c : Symbol(c, Decl(privateNameInInExpression.ts, 13, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>c : Symbol(c, Decl(privateNameInInExpression.ts, 11, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) const d = #field in (v as Foo); // Good d is boolean (not true) ->d : Symbol(d, Decl(privateNameInInExpression.ts, 15, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>d : Symbol(d, Decl(privateNameInInExpression.ts, 13, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) const e = #field in (v as never); // Good e is boolean ->e : Symbol(e, Decl(privateNameInInExpression.ts, 17, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>e : Symbol(e, Decl(privateNameInInExpression.ts, 15, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) const f = #field in (v as unknown); // Bad - RHS of in must be object type or any ->f : Symbol(f, Decl(privateNameInInExpression.ts, 19, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>f : Symbol(f, Decl(privateNameInInExpression.ts, 17, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) const g = #typo in v; // Bad - Invalid privateID ->g : Symbol(g, Decl(privateNameInInExpression.ts, 21, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>g : Symbol(g, Decl(privateNameInInExpression.ts, 19, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) const h = (#field) in v; // Bad - private id is not an expression on its own ->h : Symbol(h, Decl(privateNameInInExpression.ts, 23, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>h : Symbol(h, Decl(privateNameInInExpression.ts, 21, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) for (#field in v) { /* no-op */ } // Bad - 'in' not allowed ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any ->x : Symbol(x, Decl(privateNameInInExpression.ts, 27, 16)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>x : Symbol(x, Decl(privateNameInInExpression.ts, 25, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid ->x : Symbol(x, Decl(privateNameInInExpression.ts, 29, 16)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 8, 11)) +>x : Symbol(x, Decl(privateNameInInExpression.ts, 27, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) } whitespace(v: any) { ->whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 31, 5)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 32, 15)) +>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, 33, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 32, 15)) +>a : Symbol(a, Decl(privateNameInInExpression.ts, 31, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15)) /*2*/in/*3*/ /*4*/v/*5*/ ->v : Symbol(v, Decl(privateNameInInExpression.ts, 32, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15)) } flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { ->flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 36, 5)) ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) ->n : Symbol(n, Decl(privateNameInInExpression.ts, 37, 20)) ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) +>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, 105, 48)) ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 103, 1)) ->b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 105, 48)) ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 103, 1)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 105, 48)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) if (typeof u === 'object') { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) if (#field in n) { ->n : Symbol(n, Decl(privateNameInInExpression.ts, 37, 20)) +>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20)) n; // good n is never ->n : Symbol(n, Decl(privateNameInInExpression.ts, 37, 20)) +>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20)) } if (#field in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) } else { u; // good u is object | null ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) } if (u !== null) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) if (#field in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) } else { u; // good u is object ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) } if (#method in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) } if (#staticField in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is typeof Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) } if (#staticMethod in u) { ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is typeof Foo ->u : Symbol(u, Decl(privateNameInInExpression.ts, 37, 9)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) } } } if (#field in fb) { ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) fb; // good fb is Foo ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) } else { fb; // good fb is Bar ->fb : Symbol(fb, Decl(privateNameInInExpression.ts, 37, 30)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) } if (#field in fs) { ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) fs; // good fs is FooSub ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) } else { fs; // good fs is never ->fs : Symbol(fs, Decl(privateNameInInExpression.ts, 37, 45)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) } if (#field in b) { ->b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) b; // good b is 'Bar & Foo' ->b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) } else { b; // good b is Bar ->b : Symbol(b, Decl(privateNameInInExpression.ts, 37, 57)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) } if (#field in fsb) { ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) fsb; // good fsb is FooSub ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) } else { fsb; // good fsb is Bar ->fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 37, 65)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) } class Nested { ->Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 93, 9)) +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 91, 9)) m(v: any) { ->m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 95, 22)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 96, 14)) +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 93, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) if (#field in v) { ->v : Symbol(v, Decl(privateNameInInExpression.ts, 96, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) v; // good v is Foo ->v : Symbol(v, Decl(privateNameInInExpression.ts, 96, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) } } } @@ -213,22 +211,20 @@ class Foo { } class FooSub extends Foo { subTypeOfFoo = true } ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 103, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 105, 26)) +>subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 103, 26)) class Bar { notFoo = true } ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 105, 48)) ->notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 106, 11)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 104, 11)) -function error(v: Foo) { ->error : Symbol(error, Decl(privateNameInInExpression.ts, 106, 27)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 108, 15)) +function badSyntax(v: Foo) { +>badSyntax : Symbol(badSyntax, Decl(privateNameInInExpression.ts, 104, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 106, 19)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) return #field in v; // Bad - outside of class ->v : Symbol(v, Decl(privateNameInInExpression.ts, 108, 15)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 106, 19)) } -export { } - diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 570e93014918d..1a6e38fe5384b 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -1,6 +1,4 @@ === tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === -// TODO(aclaymore) split up into separate cases - class Foo { >Foo : Foo @@ -262,8 +260,8 @@ class Bar { notFoo = true } >notFoo : boolean >true : true -function error(v: Foo) { ->error : (v: Foo) => any +function badSyntax(v: Foo) { +>badSyntax : (v: Foo) => any >v : Foo return #field in v; // Bad - outside of class @@ -271,5 +269,3 @@ function error(v: Foo) { >v : Foo } -export { } - diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 037ac9eb08b53..869b7b8b8b5fd 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -1,8 +1,6 @@ // @strict: true // @target: esnext -// TODO(aclaymore) split up into separate cases - class Foo { #field = 1; static #staticField = 2; @@ -109,8 +107,6 @@ class Foo { class FooSub extends Foo { subTypeOfFoo = true } class Bar { notFoo = true } -function error(v: Foo) { +function badSyntax(v: Foo) { return #field in v; // Bad - outside of class } - -export { } From 85a367b1ecada8781a34e6ec2c9dad28dd5e4901 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 30 Mar 2021 12:14:32 +0100 Subject: [PATCH 25/29] add quickFix Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 34 ++++++++-- src/services/codefixes/fixSpelling.ts | 5 ++ .../privateNameInInExpression.errors.txt | 46 ++++++------- .../reference/privateNameInInExpression.js | 54 +++++++-------- .../privateNameInInExpression.symbols | 65 ++++++++++--------- .../reference/privateNameInInExpression.types | 51 ++++++++------- .../privateNames/privateNameInInExpression.ts | 28 ++++---- .../codeFixSpellingPrivateNameInIn.ts | 19 ++++++ 8 files changed, 179 insertions(+), 123 deletions(-) create mode 100644 tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 516f77ca2f5f1..88722dec679f2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27303,6 +27303,7 @@ namespace ts { } function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { + const originalName = name; let props = getPropertiesOfType(containingType); if (typeof name !== "string") { const parent = name.parent; @@ -27311,7 +27312,23 @@ namespace ts { } name = idText(name); } - return getSpellingSuggestionForName(name, props, SymbolFlags.Value); + const suggestion = getSpellingSuggestionForName(name, props, SymbolFlags.Value); + if (suggestion) { + return suggestion; + } + // If we have `#typo in expr` then we can still look up potential privateIdentifiers from the surrounding classes + if (typeof originalName !== "string" && isPrivateIdentifierInInExpression(originalName.parent)) { + const privateIdentifiers: Symbol[] = []; + forEachEnclosingClass(originalName, (klass: ClassLikeDeclaration) => { + forEach(klass.members, member => { + if (isPrivateIdentifierClassElementDeclaration(member)) { + privateIdentifiers.push(member.symbol); + } + }); + }); + return getSpellingSuggestionForName(name, privateIdentifiers, SymbolFlags.Value); + } + return undefined; } function getSuggestedSymbolForNonexistentJSXAttribute(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { @@ -31337,14 +31354,23 @@ namespace ts { function checkPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, checkMode?: CheckMode) { const privateId = node.name; + const exp = node.expression; + let rightType = checkExpression(exp, checkMode); + const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); if (lexicallyScopedSymbol === undefined) { if (!getContainingClass(node)) { error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); } else { - // TODO(aclamore): suggest similar name - error(node, Diagnostics.Cannot_find_name_0, diagnosticName(privateId)); + const suggestion = getSuggestedSymbolForNonexistentProperty(privateId, rightType); + if (suggestion) { + const suggestedName = symbolName(suggestion); + error(privateId, Diagnostics.Cannot_find_name_0_Did_you_mean_1, diagnosticName(privateId), suggestedName); + } + else { + error(privateId, Diagnostics.Cannot_find_name_0, diagnosticName(privateId)); + } } return anyType; } @@ -31352,8 +31378,6 @@ namespace ts { markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); getNodeLinks(node).resolvedSymbol = lexicallyScopedSymbol; - const exp = node.expression; - let rightType = checkExpression(exp, checkMode); if (rightType === silentNeverType) { return silentNeverType; } diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 6d80e1f900899..36f21a2652d8e 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -53,6 +53,11 @@ namespace ts.codefix { } suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType); } + else if (isPrivateIdentifierInInExpression(parent) && parent.name === node) { + const receiverType = checker.getTypeAtLocation(parent.expression); + Debug.assert(isPrivateIdentifier(node), "Expected a privateIdentifier for spelling (in)"); + suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, receiverType); + } else if (isQualifiedName(parent) && parent.right === node) { const symbol = checker.getSymbolAtLocation(parent.left); if (symbol && symbol.flags & SymbolFlags.Module) { diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 5460b9288cfa2..8306cd3a30358 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,11 +1,11 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(3,27): 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/privateNameInInExpression.ts(18,29): error TS2571: Object is of type 'unknown'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(20,19): error TS2304: Cannot find name '#typo'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,20): error TS2304: Cannot find name '#field'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(22,20): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,14): error TS2304: Cannot find name '#field'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(24,14): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(26,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(21,29): error TS2571: Object is of type 'unknown'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2552: Cannot find name '#fiel'. Did you mean '#field'? +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS18016: Private identifiers are not allowed outside class bodies. +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(108,12): error TS18016: Private identifiers are not allowed outside class bodies. @@ -19,43 +19,43 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t #method() {} static #staticMethod() {} - basics(v: any) { - const a = #field in v; // Good - a is boolean + goodRhs(v: any) { + const a = #field in v; - const b = #field in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; - const c = #field in (v as {}); // Good - c is boolean + const c = #field in (v as {}); - const d = #field in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); - const e = #field in (v as never); // Good e is boolean + const e = #field in (v as never); - const f = #field in (v as unknown); // Bad - RHS of in must be object type or any + 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 g = #typo in v; // Bad - Invalid privateID - ~~~~~~~~~~ -!!! error TS2304: Cannot find name '#typo'. + const b = #fiel in v; // Bad - typo in privateID + ~~~~~ +!!! error TS2552: Cannot find name '#fiel'. Did you mean '#field'? - const h = (#field) in v; // Bad - private id is not an expression on its own + const c = (#field) in v; // Bad - privateID is not an expression on its own ~~~~~~ !!! error TS2304: Cannot find name '#field'. ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. - for (#field in v) { /* no-op */ } // Bad - 'in' not allowed + for (#field in v) { /**/ } // Bad - 'in' not allowed ~~~~~~ !!! error TS2304: Cannot find name '#field'. ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. - for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any + 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'. - - for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid - } whitespace(v: any) { const a = v && /*0*/#field/*1*/ diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 24fb7c110a4c2..19c0f96e99bea 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -5,29 +5,29 @@ class Foo { #method() {} static #staticMethod() {} - basics(v: any) { - const a = #field in v; // Good - a is boolean + goodRhs(v: any) { + const a = #field in v; - const b = #field in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; - const c = #field in (v as {}); // Good - c is boolean + const c = #field in (v as {}); - const d = #field in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); - const e = #field in (v as never); // Good e is boolean + const e = #field in (v as never); - const f = #field in (v as unknown); // Bad - RHS of in must be object type or any - - const g = #typo in v; // Bad - Invalid privateID - - const h = (#field) in v; // Bad - private id is not an expression on its own + 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 - for (#field in v) { /* no-op */ } // Bad - 'in' not allowed + const b = #fiel in v; // Bad - typo in privateID - for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any + const c = (#field) in v; // Bad - privateID is not an expression on its own - for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid + 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*/ @@ -120,18 +120,20 @@ class Foo { static #staticField; #method() { } static #staticMethod() { } - basics(v) { - const a = #field in v; // Good - a is boolean - const b = #field in v.p1.p2; // Good - b is boolean - const c = #field in v; // Good - c is boolean - const d = #field in v; // Good d is boolean (not true) - const e = #field in v; // Good e is boolean - const f = #field in v; // Bad - RHS of in must be object type or any - const g = #typo in v; // Bad - Invalid privateID - const h = (#field) in v; // Bad - private id is not an expression on its own - for (#field in v) { /* no-op */ } // Bad - 'in' not allowed - for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any - for (let x in #field in v) { /* no-op */ } // Good - weird but valid + 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*/ diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index f979d3c1bf5e5..30a02dddacea9 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -14,54 +14,57 @@ class Foo { static #staticMethod() {} >#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 3, 16)) - basics(v: any) { ->basics : Symbol(Foo.basics, Decl(privateNameInInExpression.ts, 4, 29)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) + 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; // Good - a is boolean + const a = #field in v; >a : Symbol(a, Decl(privateNameInInExpression.ts, 7, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) - const b = #field in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; >b : Symbol(b, Decl(privateNameInInExpression.ts, 9, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) - const c = #field in (v as {}); // Good - c is boolean + const c = #field in (v as {}); >c : Symbol(c, Decl(privateNameInInExpression.ts, 11, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) - const d = #field in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); >d : Symbol(d, Decl(privateNameInInExpression.ts, 13, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) - const e = #field in (v as never); // Good e is boolean + const e = #field in (v as never); >e : Symbol(e, Decl(privateNameInInExpression.ts, 15, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) - const f = #field in (v as unknown); // Bad - RHS of in must be object type or any ->f : Symbol(f, Decl(privateNameInInExpression.ts, 17, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) - - const g = #typo in v; // Bad - Invalid privateID ->g : Symbol(g, Decl(privateNameInInExpression.ts, 19, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 11)) + for (let f in #field in v as any) { /**/ } // unlikely but valid +>f : Symbol(f, Decl(privateNameInInExpression.ts, 17, 16)) +>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 h = (#field) in v; // Bad - private id is not an expression on its own ->h : Symbol(h, Decl(privateNameInInExpression.ts, 21, 13)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 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)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) - for (#field in v) { /* no-op */ } // Bad - 'in' not allowed ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 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)) - for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any ->x : Symbol(x, Decl(privateNameInInExpression.ts, 25, 16)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 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 (let x in #field in v as any) { /* no-op */ } // Good - weird but valid ->x : Symbol(x, Decl(privateNameInInExpression.ts, 27, 16)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 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)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) } whitespace(v: any) { >whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 29, 5)) diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 1a6e38fe5384b..f323b2a6a2dc3 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -16,16 +16,16 @@ class Foo { static #staticMethod() {} >#staticMethod : () => void - basics(v: any) { ->basics : (v: any) => void + goodRhs(v: any) { +>goodRhs : (v: any) => void >v : any - const a = #field in v; // Good - a is boolean + const a = #field in v; >a : boolean >#field in v : boolean >v : any - const b = #field in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; >b : boolean >#field in v.p1.p2 : boolean >v.p1.p2 : any @@ -34,61 +34,64 @@ class Foo { >p1 : any >p2 : any - const c = #field in (v as {}); // Good - c is boolean + const c = #field in (v as {}); >c : boolean >#field in (v as {}) : boolean >(v as {}) : {} >v as {} : {} >v : any - const d = #field in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); >d : boolean >#field in (v as Foo) : boolean >(v as Foo) : Foo >v as Foo : Foo >v : any - const e = #field in (v as never); // Good e is boolean + const e = #field in (v as never); >e : boolean >#field in (v as never) : boolean >(v as never) : never >v as never : never >v : any - const f = #field in (v as unknown); // Bad - RHS of in must be object type or any ->f : boolean + for (let f in #field in v as any) { /**/ } // unlikely but valid +>f : string +>#field in v as any : any +>#field in v : boolean +>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 >(v as unknown) : unknown >v as unknown : unknown >v : any - const g = #typo in v; // Bad - Invalid privateID ->g : any ->#typo in v : any + const b = #fiel in v; // Bad - typo in privateID +>b : any +>#fiel in v : any >v : any - const h = (#field) in v; // Bad - private id is not an expression on its own ->h : boolean + const c = (#field) in v; // Bad - privateID is not an expression on its own +>c : boolean >(#field) in v : boolean >(#field) : any >#field : any >v : any - for (#field in v) { /* no-op */ } // Bad - 'in' not allowed + for (#field in v) { /**/ } // Bad - 'in' not allowed >#field : any >v : any - for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any ->x : string ->#field in v : boolean ->v : any - - for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid ->x : string ->#field in v as any : any + for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any +>d : string >#field in v : boolean >v : any - } whitespace(v: any) { >whitespace : (v: any) => void diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 869b7b8b8b5fd..7ed3900a9e37a 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -7,29 +7,29 @@ class Foo { #method() {} static #staticMethod() {} - basics(v: any) { - const a = #field in v; // Good - a is boolean + goodRhs(v: any) { + const a = #field in v; - const b = #field in v.p1.p2; // Good - b is boolean + const b = #field in v.p1.p2; - const c = #field in (v as {}); // Good - c is boolean + const c = #field in (v as {}); - const d = #field in (v as Foo); // Good d is boolean (not true) + const d = #field in (v as Foo); - const e = #field in (v as never); // Good e is boolean + const e = #field in (v as never); - const f = #field in (v as unknown); // Bad - RHS of in must be object type or any - - const g = #typo in v; // Bad - Invalid privateID - - const h = (#field) in v; // Bad - private id is not an expression on its own + 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 - for (#field in v) { /* no-op */ } // Bad - 'in' not allowed + const b = #fiel in v; // Bad - typo in privateID - for (let x in #field in v) { /* no-op */ } // Bad - rhs of in should be a object/any + const c = (#field) in v; // Bad - privateID is not an expression on its own - for (let x in #field in v as any) { /* no-op */ } // Good - weird but valid + 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*/ diff --git a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts new file mode 100644 index 0000000000000..3d3e48ede332d --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts @@ -0,0 +1,19 @@ +/// + +////class A { +//// #foo: number; +//// static isA(v: any) { +//// [|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;" +}); From 02f59a38ebf1f62914dca106b9d2c29de7d8163c Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 30 Mar 2021 12:48:42 +0100 Subject: [PATCH 26/29] update typescript.d.ts baseline Signed-off-by: Ashley Claymore --- .../reference/api/tsserverlibrary.d.ts | 257 +++++++++--------- tests/baselines/reference/api/typescript.d.ts | 257 +++++++++--------- 2 files changed, 268 insertions(+), 246 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a2ff8fc1ea29a..f730010ae3c3e 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -333,123 +333,124 @@ declare namespace ts { NonNullExpression = 226, MetaProperty = 227, SyntheticExpression = 228, - TemplateSpan = 229, - SemicolonClassElement = 230, - Block = 231, - EmptyStatement = 232, - VariableStatement = 233, - ExpressionStatement = 234, - IfStatement = 235, - DoStatement = 236, - WhileStatement = 237, - ForStatement = 238, - ForInStatement = 239, - ForOfStatement = 240, - ContinueStatement = 241, - BreakStatement = 242, - ReturnStatement = 243, - WithStatement = 244, - SwitchStatement = 245, - LabeledStatement = 246, - ThrowStatement = 247, - TryStatement = 248, - DebuggerStatement = 249, - VariableDeclaration = 250, - VariableDeclarationList = 251, - FunctionDeclaration = 252, - ClassDeclaration = 253, - InterfaceDeclaration = 254, - TypeAliasDeclaration = 255, - EnumDeclaration = 256, - ModuleDeclaration = 257, - ModuleBlock = 258, - CaseBlock = 259, - NamespaceExportDeclaration = 260, - ImportEqualsDeclaration = 261, - ImportDeclaration = 262, - ImportClause = 263, - NamespaceImport = 264, - NamedImports = 265, - ImportSpecifier = 266, - ExportAssignment = 267, - ExportDeclaration = 268, - NamedExports = 269, - NamespaceExport = 270, - ExportSpecifier = 271, - MissingDeclaration = 272, - ExternalModuleReference = 273, - JsxElement = 274, - JsxSelfClosingElement = 275, - JsxOpeningElement = 276, - JsxClosingElement = 277, - JsxFragment = 278, - JsxOpeningFragment = 279, - JsxClosingFragment = 280, - JsxAttribute = 281, - JsxAttributes = 282, - JsxSpreadAttribute = 283, - JsxExpression = 284, - CaseClause = 285, - DefaultClause = 286, - HeritageClause = 287, - CatchClause = 288, - PropertyAssignment = 289, - ShorthandPropertyAssignment = 290, - SpreadAssignment = 291, - EnumMember = 292, - UnparsedPrologue = 293, - UnparsedPrepend = 294, - UnparsedText = 295, - UnparsedInternalText = 296, - UnparsedSyntheticReference = 297, - SourceFile = 298, - Bundle = 299, - UnparsedSource = 300, - InputFiles = 301, - JSDocTypeExpression = 302, - JSDocNameReference = 303, - JSDocAllType = 304, - JSDocUnknownType = 305, - JSDocNullableType = 306, - JSDocNonNullableType = 307, - JSDocOptionalType = 308, - JSDocFunctionType = 309, - JSDocVariadicType = 310, - JSDocNamepathType = 311, - JSDocComment = 312, - JSDocText = 313, - JSDocTypeLiteral = 314, - JSDocSignature = 315, - JSDocLink = 316, - JSDocTag = 317, - JSDocAugmentsTag = 318, - JSDocImplementsTag = 319, - JSDocAuthorTag = 320, - JSDocDeprecatedTag = 321, - JSDocClassTag = 322, - JSDocPublicTag = 323, - JSDocPrivateTag = 324, - JSDocProtectedTag = 325, - JSDocReadonlyTag = 326, - JSDocOverrideTag = 327, - JSDocCallbackTag = 328, - JSDocEnumTag = 329, - JSDocParameterTag = 330, - JSDocReturnTag = 331, - JSDocThisTag = 332, - JSDocTypeTag = 333, - JSDocTemplateTag = 334, - JSDocTypedefTag = 335, - JSDocSeeTag = 336, - JSDocPropertyTag = 337, - SyntaxList = 338, - NotEmittedStatement = 339, - PartiallyEmittedExpression = 340, - CommaListExpression = 341, - MergeDeclarationMarker = 342, - EndOfDeclarationMarker = 343, - SyntheticReferenceExpression = 344, - Count = 345, + PrivateIdentifierInInExpression = 229, + TemplateSpan = 230, + SemicolonClassElement = 231, + Block = 232, + EmptyStatement = 233, + VariableStatement = 234, + ExpressionStatement = 235, + IfStatement = 236, + DoStatement = 237, + WhileStatement = 238, + ForStatement = 239, + ForInStatement = 240, + ForOfStatement = 241, + ContinueStatement = 242, + BreakStatement = 243, + ReturnStatement = 244, + WithStatement = 245, + SwitchStatement = 246, + LabeledStatement = 247, + ThrowStatement = 248, + TryStatement = 249, + DebuggerStatement = 250, + VariableDeclaration = 251, + VariableDeclarationList = 252, + FunctionDeclaration = 253, + ClassDeclaration = 254, + InterfaceDeclaration = 255, + TypeAliasDeclaration = 256, + EnumDeclaration = 257, + ModuleDeclaration = 258, + ModuleBlock = 259, + CaseBlock = 260, + NamespaceExportDeclaration = 261, + ImportEqualsDeclaration = 262, + ImportDeclaration = 263, + ImportClause = 264, + NamespaceImport = 265, + NamedImports = 266, + ImportSpecifier = 267, + ExportAssignment = 268, + ExportDeclaration = 269, + NamedExports = 270, + NamespaceExport = 271, + ExportSpecifier = 272, + MissingDeclaration = 273, + ExternalModuleReference = 274, + JsxElement = 275, + JsxSelfClosingElement = 276, + JsxOpeningElement = 277, + JsxClosingElement = 278, + JsxFragment = 279, + JsxOpeningFragment = 280, + JsxClosingFragment = 281, + JsxAttribute = 282, + JsxAttributes = 283, + JsxSpreadAttribute = 284, + JsxExpression = 285, + CaseClause = 286, + DefaultClause = 287, + HeritageClause = 288, + CatchClause = 289, + PropertyAssignment = 290, + ShorthandPropertyAssignment = 291, + SpreadAssignment = 292, + EnumMember = 293, + UnparsedPrologue = 294, + UnparsedPrepend = 295, + UnparsedText = 296, + UnparsedInternalText = 297, + UnparsedSyntheticReference = 298, + SourceFile = 299, + Bundle = 300, + UnparsedSource = 301, + InputFiles = 302, + JSDocTypeExpression = 303, + JSDocNameReference = 304, + JSDocAllType = 305, + JSDocUnknownType = 306, + JSDocNullableType = 307, + JSDocNonNullableType = 308, + JSDocOptionalType = 309, + JSDocFunctionType = 310, + JSDocVariadicType = 311, + JSDocNamepathType = 312, + JSDocComment = 313, + JSDocText = 314, + JSDocTypeLiteral = 315, + JSDocSignature = 316, + JSDocLink = 317, + JSDocTag = 318, + JSDocAugmentsTag = 319, + JSDocImplementsTag = 320, + JSDocAuthorTag = 321, + JSDocDeprecatedTag = 322, + JSDocClassTag = 323, + JSDocPublicTag = 324, + JSDocPrivateTag = 325, + JSDocProtectedTag = 326, + JSDocReadonlyTag = 327, + JSDocOverrideTag = 328, + JSDocCallbackTag = 329, + JSDocEnumTag = 330, + JSDocParameterTag = 331, + JSDocReturnTag = 332, + JSDocThisTag = 333, + JSDocTypeTag = 334, + JSDocTemplateTag = 335, + JSDocTypedefTag = 336, + JSDocSeeTag = 337, + JSDocPropertyTag = 338, + SyntaxList = 339, + NotEmittedStatement = 340, + PartiallyEmittedExpression = 341, + CommaListExpression = 342, + MergeDeclarationMarker = 343, + EndOfDeclarationMarker = 344, + SyntheticReferenceExpression = 345, + Count = 346, FirstAssignment = 62, LastAssignment = 77, FirstCompoundAssignment = 63, @@ -474,13 +475,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 77, - FirstStatement = 233, - LastStatement = 249, + FirstStatement = 234, + LastStatement = 250, FirstNode = 158, - FirstJSDocNode = 302, - LastJSDocNode = 337, - FirstJSDocTagNode = 317, - LastJSDocTagNode = 337, + FirstJSDocNode = 303, + LastJSDocNode = 338, + FirstJSDocTagNode = 318, + LastJSDocTagNode = 338, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -592,6 +593,7 @@ declare namespace ts { } export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; + export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; /** @deprecated Use `AssertsKeyword` instead. */ @@ -1251,6 +1253,12 @@ declare namespace ts { readonly expression: SuperExpression; } export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; + export interface PrivateIdentifierInInExpression extends Expression { + readonly kind: SyntaxKind.PrivateIdentifierInInExpression; + readonly name: PrivateIdentifier; + readonly inToken: Token; + readonly expression: Expression; + } export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -3394,6 +3402,8 @@ declare namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; + createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; + updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; createTemplateSpan(expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; createSemicolonClassElement(): SemicolonClassElement; @@ -4484,6 +4494,7 @@ declare namespace ts { function isSyntheticExpression(node: Node): node is SyntheticExpression; function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression; function isCommaListExpression(node: Node): node is CommaListExpression; + function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression; function isTemplateSpan(node: Node): node is TemplateSpan; function isSemicolonClassElement(node: Node): node is SemicolonClassElement; function isBlock(node: Node): node is Block; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 0b2d291eeddb5..e1f2feda69fe0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -333,123 +333,124 @@ declare namespace ts { NonNullExpression = 226, MetaProperty = 227, SyntheticExpression = 228, - TemplateSpan = 229, - SemicolonClassElement = 230, - Block = 231, - EmptyStatement = 232, - VariableStatement = 233, - ExpressionStatement = 234, - IfStatement = 235, - DoStatement = 236, - WhileStatement = 237, - ForStatement = 238, - ForInStatement = 239, - ForOfStatement = 240, - ContinueStatement = 241, - BreakStatement = 242, - ReturnStatement = 243, - WithStatement = 244, - SwitchStatement = 245, - LabeledStatement = 246, - ThrowStatement = 247, - TryStatement = 248, - DebuggerStatement = 249, - VariableDeclaration = 250, - VariableDeclarationList = 251, - FunctionDeclaration = 252, - ClassDeclaration = 253, - InterfaceDeclaration = 254, - TypeAliasDeclaration = 255, - EnumDeclaration = 256, - ModuleDeclaration = 257, - ModuleBlock = 258, - CaseBlock = 259, - NamespaceExportDeclaration = 260, - ImportEqualsDeclaration = 261, - ImportDeclaration = 262, - ImportClause = 263, - NamespaceImport = 264, - NamedImports = 265, - ImportSpecifier = 266, - ExportAssignment = 267, - ExportDeclaration = 268, - NamedExports = 269, - NamespaceExport = 270, - ExportSpecifier = 271, - MissingDeclaration = 272, - ExternalModuleReference = 273, - JsxElement = 274, - JsxSelfClosingElement = 275, - JsxOpeningElement = 276, - JsxClosingElement = 277, - JsxFragment = 278, - JsxOpeningFragment = 279, - JsxClosingFragment = 280, - JsxAttribute = 281, - JsxAttributes = 282, - JsxSpreadAttribute = 283, - JsxExpression = 284, - CaseClause = 285, - DefaultClause = 286, - HeritageClause = 287, - CatchClause = 288, - PropertyAssignment = 289, - ShorthandPropertyAssignment = 290, - SpreadAssignment = 291, - EnumMember = 292, - UnparsedPrologue = 293, - UnparsedPrepend = 294, - UnparsedText = 295, - UnparsedInternalText = 296, - UnparsedSyntheticReference = 297, - SourceFile = 298, - Bundle = 299, - UnparsedSource = 300, - InputFiles = 301, - JSDocTypeExpression = 302, - JSDocNameReference = 303, - JSDocAllType = 304, - JSDocUnknownType = 305, - JSDocNullableType = 306, - JSDocNonNullableType = 307, - JSDocOptionalType = 308, - JSDocFunctionType = 309, - JSDocVariadicType = 310, - JSDocNamepathType = 311, - JSDocComment = 312, - JSDocText = 313, - JSDocTypeLiteral = 314, - JSDocSignature = 315, - JSDocLink = 316, - JSDocTag = 317, - JSDocAugmentsTag = 318, - JSDocImplementsTag = 319, - JSDocAuthorTag = 320, - JSDocDeprecatedTag = 321, - JSDocClassTag = 322, - JSDocPublicTag = 323, - JSDocPrivateTag = 324, - JSDocProtectedTag = 325, - JSDocReadonlyTag = 326, - JSDocOverrideTag = 327, - JSDocCallbackTag = 328, - JSDocEnumTag = 329, - JSDocParameterTag = 330, - JSDocReturnTag = 331, - JSDocThisTag = 332, - JSDocTypeTag = 333, - JSDocTemplateTag = 334, - JSDocTypedefTag = 335, - JSDocSeeTag = 336, - JSDocPropertyTag = 337, - SyntaxList = 338, - NotEmittedStatement = 339, - PartiallyEmittedExpression = 340, - CommaListExpression = 341, - MergeDeclarationMarker = 342, - EndOfDeclarationMarker = 343, - SyntheticReferenceExpression = 344, - Count = 345, + PrivateIdentifierInInExpression = 229, + TemplateSpan = 230, + SemicolonClassElement = 231, + Block = 232, + EmptyStatement = 233, + VariableStatement = 234, + ExpressionStatement = 235, + IfStatement = 236, + DoStatement = 237, + WhileStatement = 238, + ForStatement = 239, + ForInStatement = 240, + ForOfStatement = 241, + ContinueStatement = 242, + BreakStatement = 243, + ReturnStatement = 244, + WithStatement = 245, + SwitchStatement = 246, + LabeledStatement = 247, + ThrowStatement = 248, + TryStatement = 249, + DebuggerStatement = 250, + VariableDeclaration = 251, + VariableDeclarationList = 252, + FunctionDeclaration = 253, + ClassDeclaration = 254, + InterfaceDeclaration = 255, + TypeAliasDeclaration = 256, + EnumDeclaration = 257, + ModuleDeclaration = 258, + ModuleBlock = 259, + CaseBlock = 260, + NamespaceExportDeclaration = 261, + ImportEqualsDeclaration = 262, + ImportDeclaration = 263, + ImportClause = 264, + NamespaceImport = 265, + NamedImports = 266, + ImportSpecifier = 267, + ExportAssignment = 268, + ExportDeclaration = 269, + NamedExports = 270, + NamespaceExport = 271, + ExportSpecifier = 272, + MissingDeclaration = 273, + ExternalModuleReference = 274, + JsxElement = 275, + JsxSelfClosingElement = 276, + JsxOpeningElement = 277, + JsxClosingElement = 278, + JsxFragment = 279, + JsxOpeningFragment = 280, + JsxClosingFragment = 281, + JsxAttribute = 282, + JsxAttributes = 283, + JsxSpreadAttribute = 284, + JsxExpression = 285, + CaseClause = 286, + DefaultClause = 287, + HeritageClause = 288, + CatchClause = 289, + PropertyAssignment = 290, + ShorthandPropertyAssignment = 291, + SpreadAssignment = 292, + EnumMember = 293, + UnparsedPrologue = 294, + UnparsedPrepend = 295, + UnparsedText = 296, + UnparsedInternalText = 297, + UnparsedSyntheticReference = 298, + SourceFile = 299, + Bundle = 300, + UnparsedSource = 301, + InputFiles = 302, + JSDocTypeExpression = 303, + JSDocNameReference = 304, + JSDocAllType = 305, + JSDocUnknownType = 306, + JSDocNullableType = 307, + JSDocNonNullableType = 308, + JSDocOptionalType = 309, + JSDocFunctionType = 310, + JSDocVariadicType = 311, + JSDocNamepathType = 312, + JSDocComment = 313, + JSDocText = 314, + JSDocTypeLiteral = 315, + JSDocSignature = 316, + JSDocLink = 317, + JSDocTag = 318, + JSDocAugmentsTag = 319, + JSDocImplementsTag = 320, + JSDocAuthorTag = 321, + JSDocDeprecatedTag = 322, + JSDocClassTag = 323, + JSDocPublicTag = 324, + JSDocPrivateTag = 325, + JSDocProtectedTag = 326, + JSDocReadonlyTag = 327, + JSDocOverrideTag = 328, + JSDocCallbackTag = 329, + JSDocEnumTag = 330, + JSDocParameterTag = 331, + JSDocReturnTag = 332, + JSDocThisTag = 333, + JSDocTypeTag = 334, + JSDocTemplateTag = 335, + JSDocTypedefTag = 336, + JSDocSeeTag = 337, + JSDocPropertyTag = 338, + SyntaxList = 339, + NotEmittedStatement = 340, + PartiallyEmittedExpression = 341, + CommaListExpression = 342, + MergeDeclarationMarker = 343, + EndOfDeclarationMarker = 344, + SyntheticReferenceExpression = 345, + Count = 346, FirstAssignment = 62, LastAssignment = 77, FirstCompoundAssignment = 63, @@ -474,13 +475,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 77, - FirstStatement = 233, - LastStatement = 249, + FirstStatement = 234, + LastStatement = 250, FirstNode = 158, - FirstJSDocNode = 302, - LastJSDocNode = 337, - FirstJSDocTagNode = 317, - LastJSDocTagNode = 337, + FirstJSDocNode = 303, + LastJSDocNode = 338, + FirstJSDocTagNode = 318, + LastJSDocTagNode = 338, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -592,6 +593,7 @@ declare namespace ts { } export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; + export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; /** @deprecated Use `AssertsKeyword` instead. */ @@ -1251,6 +1253,12 @@ declare namespace ts { readonly expression: SuperExpression; } export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; + export interface PrivateIdentifierInInExpression extends Expression { + readonly kind: SyntaxKind.PrivateIdentifierInInExpression; + readonly name: PrivateIdentifier; + readonly inToken: Token; + readonly expression: Expression; + } export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -3394,6 +3402,8 @@ declare namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; + createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; + updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; createTemplateSpan(expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; createSemicolonClassElement(): SemicolonClassElement; @@ -4484,6 +4494,7 @@ declare namespace ts { function isSyntheticExpression(node: Node): node is SyntheticExpression; function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression; function isCommaListExpression(node: Node): node is CommaListExpression; + function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression; function isTemplateSpan(node: Node): node is TemplateSpan; function isSemicolonClassElement(node: Node): node is SemicolonClassElement; function isBlock(node: Node): node is Block; From 89280395aebebbc04d53ccf2e02b0e5a19e45105 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 24 May 2021 15:58:29 +0100 Subject: [PATCH 27/29] post PR review improvements: - simplify obtaining private fields's static or instance type - make visitor node test more specific - clean up warnings about static class fields in tests Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 11 +++++------ src/compiler/transformers/classFields.ts | 1 - src/compiler/utilities.ts | 4 +--- src/compiler/visitorPublic.ts | 2 +- .../privateNameInInExpression.errors.txt | 5 +---- .../reference/privateNameInInExpression.js | 17 ++++------------- .../privateNames/privateNameInInExpression.ts | 1 + 7 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 88722dec679f2..d8289c818cefa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23245,16 +23245,15 @@ namespace ts { } const classSymbol = symbol.parent!; const classType = getTypeOfSymbol(classSymbol); - const firstDecl = symbol.declarations?.[0]; - Debug.assert(firstDecl, "should always have a declaration"); + const classDecl = symbol.valueDeclaration; + Debug.assert(classDecl, "should always have a declaration"); let targetType: Type; - if (hasSyntacticModifier(firstDecl, ModifierFlags.Static)) { + if (hasStaticModifier(classDecl)) { targetType = classType; } else { - const ctorSigs = getSignaturesOfType(classType, SignatureKind.Construct); - Debug.assert(ctorSigs.length > 0, "should always have a constructor"); - targetType = getReturnTypeOfSignature(ctorSigs[0]); + const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); + targetType = classInstanceType; } return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); } diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 476ce7eec7569..bcff24a07e2d9 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -220,7 +220,6 @@ namespace ts { } // Private name has not been declared. Subsequent transformers will handle this error - // TODO(aclaymore): confirm this is how we want to handle the error return visitEachChild(node, visitor, context); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8ee4a4d08e548..420335b7b2c4d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3580,9 +3580,6 @@ namespace ts { return getBinaryOperatorPrecedence(operatorKind); } - case SyntaxKind.PrivateIdentifierInInExpression: - return OperatorPrecedence.Relational; - // TODO: Should prefix `++` and `--` be moved to the `Update` precedence? case SyntaxKind.TypeAssertionExpression: case SyntaxKind.NonNullExpression: @@ -3609,6 +3606,7 @@ namespace ts { return OperatorPrecedence.Member; case SyntaxKind.AsExpression: + case SyntaxKind.PrivateIdentifierInInExpression: return OperatorPrecedence.Relational; case SyntaxKind.ThisKeyword: diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 8246d4440d013..790f96965ffb7 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -795,7 +795,7 @@ namespace ts { case SyntaxKind.PrivateIdentifierInInExpression: return factory.updatePrivateIdentifierInInExpression(node, - nodeVisitor((node).name, visitor, isExpression), + nodeVisitor((node).name, visitor, isMemberName), nodeVisitor((node).inToken, tokenVisitor, isToken), nodeVisitor((node).expression, visitor, isExpression)); diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 8306cd3a30358..66152059feb4e 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,4 +1,3 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(3,27): 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/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2552: Cannot find name '#fiel'. Did you mean '#field'? tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS2304: Cannot find name '#field'. @@ -10,12 +9,10 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(108,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (10 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== class Foo { #field = 1; 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. #method() {} static #staticMethod() {} diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 19c0f96e99bea..000620cb6d561 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -113,11 +113,8 @@ function badSyntax(v: Foo) { //// [privateNameInInExpression.js] "use strict"; class Foo { - constructor() { - this.#field = 1; - } - #field; - static #staticField; + #field = 1; + static #staticField = 2; #method() { } static #staticMethod() { } goodRhs(v) { @@ -202,17 +199,11 @@ class Foo { } } } -Foo.#staticField = 2; class FooSub extends Foo { - constructor() { - super(...arguments); - this.subTypeOfFoo = true; - } + subTypeOfFoo = true; } class Bar { - constructor() { - this.notFoo = true; - } + notFoo = true; } function badSyntax(v) { return #field in v; // Bad - outside of class diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index 7ed3900a9e37a..abea3ed544e64 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -1,5 +1,6 @@ // @strict: true // @target: esnext +// @useDefineForClassFields: true class Foo { #field = 1; From a846027cf59c6d2a651eba5072c9f112b0c29b9d Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 9 Jun 2021 19:30:24 +0100 Subject: [PATCH 28/29] fix linting Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 2 +- src/compiler/visitorPublic.ts | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4c8a1a87f606c..4b4e35c826cb9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23602,7 +23602,7 @@ namespace ts { return type; } const classSymbol = symbol.parent!; - const classType = getTypeOfSymbol(classSymbol); + const classType = getTypeOfSymbol(classSymbol) as InterfaceType; const classDecl = symbol.valueDeclaration; Debug.assert(classDecl, "should always have a declaration"); let targetType: Type; diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 04698876816ce..61bd204647187 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -794,10 +794,11 @@ namespace ts { nodeVisitor(node.right, visitor, isExpression)); case SyntaxKind.PrivateIdentifierInInExpression: - return factory.updatePrivateIdentifierInInExpression(node, - nodeVisitor((node).name, visitor, isMemberName), - nodeVisitor((node).inToken, tokenVisitor, isToken), - nodeVisitor((node).expression, visitor, isExpression)); + Debug.type(node); + return factory.updatePrivateIdentifierInInExpression(node, + nodeVisitor((node).name, visitor, isMemberName), + nodeVisitor((node).inToken, tokenVisitor, isToken), + nodeVisitor((node).expression, visitor, isExpression)); case SyntaxKind.ConditionalExpression: Debug.type(node); From d2bddf35ce29beefdd06eb954cdffbf4c9e38cbb Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 9 Jun 2021 19:48:06 +0100 Subject: [PATCH 29/29] update .d.ts baselines Signed-off-by: Ashley Claymore --- .../reference/api/tsserverlibrary.d.ts | 255 +++++++------- tests/baselines/reference/api/typescript.d.ts | 312 +++++++++++------- 2 files changed, 313 insertions(+), 254 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a603773520668..4ef574ab1f0bf 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -335,126 +335,127 @@ declare namespace ts { NonNullExpression = 227, MetaProperty = 228, SyntheticExpression = 229, - TemplateSpan = 230, - SemicolonClassElement = 231, - Block = 232, - EmptyStatement = 233, - VariableStatement = 234, - ExpressionStatement = 235, - IfStatement = 236, - DoStatement = 237, - WhileStatement = 238, - ForStatement = 239, - ForInStatement = 240, - ForOfStatement = 241, - ContinueStatement = 242, - BreakStatement = 243, - ReturnStatement = 244, - WithStatement = 245, - SwitchStatement = 246, - LabeledStatement = 247, - ThrowStatement = 248, - TryStatement = 249, - DebuggerStatement = 250, - VariableDeclaration = 251, - VariableDeclarationList = 252, - FunctionDeclaration = 253, - ClassDeclaration = 254, - InterfaceDeclaration = 255, - TypeAliasDeclaration = 256, - EnumDeclaration = 257, - ModuleDeclaration = 258, - ModuleBlock = 259, - CaseBlock = 260, - NamespaceExportDeclaration = 261, - ImportEqualsDeclaration = 262, - ImportDeclaration = 263, - ImportClause = 264, - NamespaceImport = 265, - NamedImports = 266, - ImportSpecifier = 267, - ExportAssignment = 268, - ExportDeclaration = 269, - NamedExports = 270, - NamespaceExport = 271, - ExportSpecifier = 272, - MissingDeclaration = 273, - ExternalModuleReference = 274, - JsxElement = 275, - JsxSelfClosingElement = 276, - JsxOpeningElement = 277, - JsxClosingElement = 278, - JsxFragment = 279, - JsxOpeningFragment = 280, - JsxClosingFragment = 281, - JsxAttribute = 282, - JsxAttributes = 283, - JsxSpreadAttribute = 284, - JsxExpression = 285, - CaseClause = 286, - DefaultClause = 287, - HeritageClause = 288, - CatchClause = 289, - PropertyAssignment = 290, - ShorthandPropertyAssignment = 291, - SpreadAssignment = 292, - EnumMember = 293, - UnparsedPrologue = 294, - UnparsedPrepend = 295, - UnparsedText = 296, - UnparsedInternalText = 297, - UnparsedSyntheticReference = 298, - SourceFile = 299, - Bundle = 300, - UnparsedSource = 301, - InputFiles = 302, - JSDocTypeExpression = 303, - JSDocNameReference = 304, - JSDocMemberName = 305, - JSDocAllType = 306, - JSDocUnknownType = 307, - JSDocNullableType = 308, - JSDocNonNullableType = 309, - JSDocOptionalType = 310, - JSDocFunctionType = 311, - JSDocVariadicType = 312, - JSDocNamepathType = 313, - JSDocComment = 314, - JSDocText = 315, - JSDocTypeLiteral = 316, - JSDocSignature = 317, - JSDocLink = 318, - JSDocLinkCode = 319, - JSDocLinkPlain = 320, - JSDocTag = 321, - JSDocAugmentsTag = 322, - JSDocImplementsTag = 323, - JSDocAuthorTag = 324, - JSDocDeprecatedTag = 325, - JSDocClassTag = 326, - JSDocPublicTag = 327, - JSDocPrivateTag = 328, - JSDocProtectedTag = 329, - JSDocReadonlyTag = 330, - JSDocOverrideTag = 331, - JSDocCallbackTag = 332, - JSDocEnumTag = 333, - JSDocParameterTag = 334, - JSDocReturnTag = 335, - JSDocThisTag = 336, - JSDocTypeTag = 337, - JSDocTemplateTag = 338, - JSDocTypedefTag = 339, - JSDocSeeTag = 340, - JSDocPropertyTag = 341, - SyntaxList = 342, - NotEmittedStatement = 343, - PartiallyEmittedExpression = 344, - CommaListExpression = 345, - MergeDeclarationMarker = 346, - EndOfDeclarationMarker = 347, - SyntheticReferenceExpression = 348, - Count = 349, + PrivateIdentifierInInExpression = 230, + TemplateSpan = 231, + SemicolonClassElement = 232, + Block = 233, + EmptyStatement = 234, + VariableStatement = 235, + ExpressionStatement = 236, + IfStatement = 237, + DoStatement = 238, + WhileStatement = 239, + ForStatement = 240, + ForInStatement = 241, + ForOfStatement = 242, + ContinueStatement = 243, + BreakStatement = 244, + ReturnStatement = 245, + WithStatement = 246, + SwitchStatement = 247, + LabeledStatement = 248, + ThrowStatement = 249, + TryStatement = 250, + DebuggerStatement = 251, + VariableDeclaration = 252, + VariableDeclarationList = 253, + FunctionDeclaration = 254, + ClassDeclaration = 255, + InterfaceDeclaration = 256, + TypeAliasDeclaration = 257, + EnumDeclaration = 258, + ModuleDeclaration = 259, + ModuleBlock = 260, + CaseBlock = 261, + NamespaceExportDeclaration = 262, + ImportEqualsDeclaration = 263, + ImportDeclaration = 264, + ImportClause = 265, + NamespaceImport = 266, + NamedImports = 267, + ImportSpecifier = 268, + ExportAssignment = 269, + ExportDeclaration = 270, + NamedExports = 271, + NamespaceExport = 272, + ExportSpecifier = 273, + MissingDeclaration = 274, + ExternalModuleReference = 275, + JsxElement = 276, + JsxSelfClosingElement = 277, + JsxOpeningElement = 278, + JsxClosingElement = 279, + JsxFragment = 280, + JsxOpeningFragment = 281, + JsxClosingFragment = 282, + JsxAttribute = 283, + JsxAttributes = 284, + JsxSpreadAttribute = 285, + JsxExpression = 286, + CaseClause = 287, + DefaultClause = 288, + HeritageClause = 289, + CatchClause = 290, + PropertyAssignment = 291, + ShorthandPropertyAssignment = 292, + SpreadAssignment = 293, + EnumMember = 294, + UnparsedPrologue = 295, + UnparsedPrepend = 296, + UnparsedText = 297, + UnparsedInternalText = 298, + UnparsedSyntheticReference = 299, + SourceFile = 300, + Bundle = 301, + UnparsedSource = 302, + InputFiles = 303, + JSDocTypeExpression = 304, + JSDocNameReference = 305, + JSDocMemberName = 306, + JSDocAllType = 307, + JSDocUnknownType = 308, + JSDocNullableType = 309, + JSDocNonNullableType = 310, + JSDocOptionalType = 311, + JSDocFunctionType = 312, + JSDocVariadicType = 313, + JSDocNamepathType = 314, + JSDocComment = 315, + JSDocText = 316, + JSDocTypeLiteral = 317, + JSDocSignature = 318, + JSDocLink = 319, + JSDocLinkCode = 320, + JSDocLinkPlain = 321, + JSDocTag = 322, + JSDocAugmentsTag = 323, + JSDocImplementsTag = 324, + JSDocAuthorTag = 325, + JSDocDeprecatedTag = 326, + JSDocClassTag = 327, + JSDocPublicTag = 328, + JSDocPrivateTag = 329, + JSDocProtectedTag = 330, + JSDocReadonlyTag = 331, + JSDocOverrideTag = 332, + JSDocCallbackTag = 333, + JSDocEnumTag = 334, + JSDocParameterTag = 335, + JSDocReturnTag = 336, + JSDocThisTag = 337, + JSDocTypeTag = 338, + JSDocTemplateTag = 339, + JSDocTypedefTag = 340, + JSDocSeeTag = 341, + JSDocPropertyTag = 342, + SyntaxList = 343, + NotEmittedStatement = 344, + PartiallyEmittedExpression = 345, + CommaListExpression = 346, + MergeDeclarationMarker = 347, + EndOfDeclarationMarker = 348, + SyntheticReferenceExpression = 349, + Count = 350, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -479,13 +480,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 234, - LastStatement = 250, + FirstStatement = 235, + LastStatement = 251, FirstNode = 159, - FirstJSDocNode = 303, - LastJSDocNode = 341, - FirstJSDocTagNode = 321, - LastJSDocTagNode = 341, + FirstJSDocNode = 304, + LastJSDocNode = 342, + FirstJSDocTagNode = 322, + LastJSDocTagNode = 342, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -11185,4 +11186,4 @@ declare namespace ts { } export = ts; -export as namespace ts; +export as namespace ts; \ No newline at end of file diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index bc31ab69ec4bd..f43cc392ad545 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -335,126 +335,127 @@ declare namespace ts { NonNullExpression = 227, MetaProperty = 228, SyntheticExpression = 229, - TemplateSpan = 230, - SemicolonClassElement = 231, - Block = 232, - EmptyStatement = 233, - VariableStatement = 234, - ExpressionStatement = 235, - IfStatement = 236, - DoStatement = 237, - WhileStatement = 238, - ForStatement = 239, - ForInStatement = 240, - ForOfStatement = 241, - ContinueStatement = 242, - BreakStatement = 243, - ReturnStatement = 244, - WithStatement = 245, - SwitchStatement = 246, - LabeledStatement = 247, - ThrowStatement = 248, - TryStatement = 249, - DebuggerStatement = 250, - VariableDeclaration = 251, - VariableDeclarationList = 252, - FunctionDeclaration = 253, - ClassDeclaration = 254, - InterfaceDeclaration = 255, - TypeAliasDeclaration = 256, - EnumDeclaration = 257, - ModuleDeclaration = 258, - ModuleBlock = 259, - CaseBlock = 260, - NamespaceExportDeclaration = 261, - ImportEqualsDeclaration = 262, - ImportDeclaration = 263, - ImportClause = 264, - NamespaceImport = 265, - NamedImports = 266, - ImportSpecifier = 267, - ExportAssignment = 268, - ExportDeclaration = 269, - NamedExports = 270, - NamespaceExport = 271, - ExportSpecifier = 272, - MissingDeclaration = 273, - ExternalModuleReference = 274, - JsxElement = 275, - JsxSelfClosingElement = 276, - JsxOpeningElement = 277, - JsxClosingElement = 278, - JsxFragment = 279, - JsxOpeningFragment = 280, - JsxClosingFragment = 281, - JsxAttribute = 282, - JsxAttributes = 283, - JsxSpreadAttribute = 284, - JsxExpression = 285, - CaseClause = 286, - DefaultClause = 287, - HeritageClause = 288, - CatchClause = 289, - PropertyAssignment = 290, - ShorthandPropertyAssignment = 291, - SpreadAssignment = 292, - EnumMember = 293, - UnparsedPrologue = 294, - UnparsedPrepend = 295, - UnparsedText = 296, - UnparsedInternalText = 297, - UnparsedSyntheticReference = 298, - SourceFile = 299, - Bundle = 300, - UnparsedSource = 301, - InputFiles = 302, - JSDocTypeExpression = 303, - JSDocNameReference = 304, - JSDocMemberName = 305, - JSDocAllType = 306, - JSDocUnknownType = 307, - JSDocNullableType = 308, - JSDocNonNullableType = 309, - JSDocOptionalType = 310, - JSDocFunctionType = 311, - JSDocVariadicType = 312, - JSDocNamepathType = 313, - JSDocComment = 314, - JSDocText = 315, - JSDocTypeLiteral = 316, - JSDocSignature = 317, - JSDocLink = 318, - JSDocLinkCode = 319, - JSDocLinkPlain = 320, - JSDocTag = 321, - JSDocAugmentsTag = 322, - JSDocImplementsTag = 323, - JSDocAuthorTag = 324, - JSDocDeprecatedTag = 325, - JSDocClassTag = 326, - JSDocPublicTag = 327, - JSDocPrivateTag = 328, - JSDocProtectedTag = 329, - JSDocReadonlyTag = 330, - JSDocOverrideTag = 331, - JSDocCallbackTag = 332, - JSDocEnumTag = 333, - JSDocParameterTag = 334, - JSDocReturnTag = 335, - JSDocThisTag = 336, - JSDocTypeTag = 337, - JSDocTemplateTag = 338, - JSDocTypedefTag = 339, - JSDocSeeTag = 340, - JSDocPropertyTag = 341, - SyntaxList = 342, - NotEmittedStatement = 343, - PartiallyEmittedExpression = 344, - CommaListExpression = 345, - MergeDeclarationMarker = 346, - EndOfDeclarationMarker = 347, - SyntheticReferenceExpression = 348, - Count = 349, + PrivateIdentifierInInExpression = 230, + TemplateSpan = 231, + SemicolonClassElement = 232, + Block = 233, + EmptyStatement = 234, + VariableStatement = 235, + ExpressionStatement = 236, + IfStatement = 237, + DoStatement = 238, + WhileStatement = 239, + ForStatement = 240, + ForInStatement = 241, + ForOfStatement = 242, + ContinueStatement = 243, + BreakStatement = 244, + ReturnStatement = 245, + WithStatement = 246, + SwitchStatement = 247, + LabeledStatement = 248, + ThrowStatement = 249, + TryStatement = 250, + DebuggerStatement = 251, + VariableDeclaration = 252, + VariableDeclarationList = 253, + FunctionDeclaration = 254, + ClassDeclaration = 255, + InterfaceDeclaration = 256, + TypeAliasDeclaration = 257, + EnumDeclaration = 258, + ModuleDeclaration = 259, + ModuleBlock = 260, + CaseBlock = 261, + NamespaceExportDeclaration = 262, + ImportEqualsDeclaration = 263, + ImportDeclaration = 264, + ImportClause = 265, + NamespaceImport = 266, + NamedImports = 267, + ImportSpecifier = 268, + ExportAssignment = 269, + ExportDeclaration = 270, + NamedExports = 271, + NamespaceExport = 272, + ExportSpecifier = 273, + MissingDeclaration = 274, + ExternalModuleReference = 275, + JsxElement = 276, + JsxSelfClosingElement = 277, + JsxOpeningElement = 278, + JsxClosingElement = 279, + JsxFragment = 280, + JsxOpeningFragment = 281, + JsxClosingFragment = 282, + JsxAttribute = 283, + JsxAttributes = 284, + JsxSpreadAttribute = 285, + JsxExpression = 286, + CaseClause = 287, + DefaultClause = 288, + HeritageClause = 289, + CatchClause = 290, + PropertyAssignment = 291, + ShorthandPropertyAssignment = 292, + SpreadAssignment = 293, + EnumMember = 294, + UnparsedPrologue = 295, + UnparsedPrepend = 296, + UnparsedText = 297, + UnparsedInternalText = 298, + UnparsedSyntheticReference = 299, + SourceFile = 300, + Bundle = 301, + UnparsedSource = 302, + InputFiles = 303, + JSDocTypeExpression = 304, + JSDocNameReference = 305, + JSDocMemberName = 306, + JSDocAllType = 307, + JSDocUnknownType = 308, + JSDocNullableType = 309, + JSDocNonNullableType = 310, + JSDocOptionalType = 311, + JSDocFunctionType = 312, + JSDocVariadicType = 313, + JSDocNamepathType = 314, + JSDocComment = 315, + JSDocText = 316, + JSDocTypeLiteral = 317, + JSDocSignature = 318, + JSDocLink = 319, + JSDocLinkCode = 320, + JSDocLinkPlain = 321, + JSDocTag = 322, + JSDocAugmentsTag = 323, + JSDocImplementsTag = 324, + JSDocAuthorTag = 325, + JSDocDeprecatedTag = 326, + JSDocClassTag = 327, + JSDocPublicTag = 328, + JSDocPrivateTag = 329, + JSDocProtectedTag = 330, + JSDocReadonlyTag = 331, + JSDocOverrideTag = 332, + JSDocCallbackTag = 333, + JSDocEnumTag = 334, + JSDocParameterTag = 335, + JSDocReturnTag = 336, + JSDocThisTag = 337, + JSDocTypeTag = 338, + JSDocTemplateTag = 339, + JSDocTypedefTag = 340, + JSDocSeeTag = 341, + JSDocPropertyTag = 342, + SyntaxList = 343, + NotEmittedStatement = 344, + PartiallyEmittedExpression = 345, + CommaListExpression = 346, + MergeDeclarationMarker = 347, + EndOfDeclarationMarker = 348, + SyntheticReferenceExpression = 349, + Count = 350, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -479,13 +480,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 234, - LastStatement = 250, + FirstStatement = 235, + LastStatement = 251, FirstNode = 159, - FirstJSDocNode = 303, - LastJSDocNode = 341, - FirstJSDocTagNode = 321, - LastJSDocTagNode = 341, + FirstJSDocNode = 304, + LastJSDocNode = 342, + FirstJSDocTagNode = 322, + LastJSDocTagNode = 342, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -513,6 +514,62 @@ declare namespace ts { HasAsyncFunctions = 2048, DisallowInContext = 4096, YieldContext = 8192, + DecoratorContext = 16384, + AwaitContext = 32768, + ThisNodeHasError = 65536, + JavaScriptFile = 131072, + ThisNodeOrAnySubNodesHasError = 262144, + HasAggregatedChildData = 524288, + JSDoc = 4194304, + JsonFile = 33554432, + BlockScoped = 3, + ReachabilityCheckFlags = 768, + ReachabilityAndEmitFlags = 2816, + ContextFlags = 25358336, + TypeExcludesFlags = 40960, + } + export enum ModifierFlags { + None = 0, + Export = 1, + Ambient = 2, + Public = 4, + Private = 8, + Protected = 16, + Static = 32, + Readonly = 64, + Abstract = 128, + Async = 256, + Default = 512, + Const = 2048, + HasComputedJSDocModifiers = 4096, + Deprecated = 8192, + Override = 16384, + HasComputedFlags = 536870912, + AccessibilityModifier = 28, + ParameterPropertyModifier = 16476, + NonPublicAccessibilityModifier = 24, + TypeScriptModifier = 18654, + ExportDefault = 513, + All = 27647 + } + export enum JsxFlags { + None = 0, + /** An element from a named property of the JSX.IntrinsicElements interface */ + IntrinsicNamedElement = 1, + /** An element inferred from the string index signature of the JSX.IntrinsicElements interface */ + IntrinsicIndexedElement = 2, + IntrinsicElement = 3 + } + export interface Node extends ReadonlyTextRange { + readonly kind: SyntaxKind; + readonly flags: NodeFlags; + readonly decorators?: NodeArray; + readonly modifiers?: ModifiersArray; + readonly parent: Node; + } + export interface JSDocContainer { + } + export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | EmptyStatement | DebuggerStatement | Block | VariableStatement | ExpressionStatement | IfStatement | DoStatement | WhileStatement | ForStatement | ForInStatement | ForOfStatement | BreakStatement | ContinueStatement | ReturnStatement | WithStatement | SwitchStatement | LabeledStatement | ThrowStatement | TryStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | EndOfFileToken; export type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType; export type HasTypeArguments = CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement | JsxSelfClosingElement; export type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; @@ -570,6 +627,7 @@ declare namespace ts { export enum GeneratedIdentifierFlags { None = 0, ReservedInNestedScopes = 8, + Optimistic = 16, FileLevel = 32, AllowNameSubstitution = 64 } @@ -7367,4 +7425,4 @@ declare namespace ts { const isIdentifierOrPrivateIdentifier: (node: Node) => node is MemberName; } -export = ts; +export = ts; \ No newline at end of file