From c2f53fcc768452ffcad3076acdcf927b0473bfe7 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Sat, 7 Sep 2019 13:53:09 -0700 Subject: [PATCH] Add grammar error for invalid tagged template, more tests --- src/compiler/checker.ts | 9 +- src/compiler/diagnosticMessages.json | 4 + src/compiler/parser.ts | 2 +- src/compiler/transformers/esnext.ts | 1 - src/testRunner/tsconfig.json | 1 + .../unittests/evaluation/optionalCall.ts | 191 ++++++++++++++++++ tests/baselines/reference/callChain.2.js | 16 ++ tests/baselines/reference/callChain.2.symbols | 28 +++ tests/baselines/reference/callChain.2.types | 31 +++ .../reference/elementAccessChain.2.js | 20 ++ .../reference/elementAccessChain.2.symbols | 43 ++++ .../reference/elementAccessChain.2.types | 48 +++++ .../reference/propertyAccessChain.2.js | 16 ++ .../reference/propertyAccessChain.2.symbols | 34 ++++ .../reference/propertyAccessChain.2.types | 34 ++++ .../reference/taggedTemplateChain.errors.txt | 13 ++ .../reference/taggedTemplateChain.js | 13 ++ .../reference/taggedTemplateChain.symbols | 10 + .../reference/taggedTemplateChain.types | 15 ++ .../optionalChaining/callChain/callChain.2.ts | 10 + .../elementAccessChain.2.ts | 12 ++ .../propertyAccessChain.2.ts | 10 + .../taggedTemplateChain.ts | 4 + 23 files changed, 562 insertions(+), 3 deletions(-) create mode 100644 src/testRunner/unittests/evaluation/optionalCall.ts create mode 100644 tests/baselines/reference/callChain.2.js create mode 100644 tests/baselines/reference/callChain.2.symbols create mode 100644 tests/baselines/reference/callChain.2.types create mode 100644 tests/baselines/reference/elementAccessChain.2.js create mode 100644 tests/baselines/reference/elementAccessChain.2.symbols create mode 100644 tests/baselines/reference/elementAccessChain.2.types create mode 100644 tests/baselines/reference/propertyAccessChain.2.js create mode 100644 tests/baselines/reference/propertyAccessChain.2.symbols create mode 100644 tests/baselines/reference/propertyAccessChain.2.types create mode 100644 tests/baselines/reference/taggedTemplateChain.errors.txt create mode 100644 tests/baselines/reference/taggedTemplateChain.js create mode 100644 tests/baselines/reference/taggedTemplateChain.symbols create mode 100644 tests/baselines/reference/taggedTemplateChain.types create mode 100644 tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts create mode 100644 tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts create mode 100644 tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts create mode 100644 tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5c81e13d92cad..25adadc34b87a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23182,7 +23182,7 @@ namespace ts { } function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type { - checkGrammarTypeArguments(node, node.typeArguments); + if (!checkGrammarTaggedTemplateChain(node)) checkGrammarTypeArguments(node, node.typeArguments); if (languageVersion < ScriptTarget.ES2015) { checkExternalEmitHelpers(node, ExternalEmitHelpers.MakeTemplateObject); } @@ -32891,6 +32891,13 @@ namespace ts { checkGrammarForAtLeastOneTypeArgument(node, typeArguments); } + function checkGrammarTaggedTemplateChain(node: TaggedTemplateExpression): boolean { + if (node.questionDotToken || node.flags & NodeFlags.OptionalChain) { + return grammarErrorOnNode(node.template, Diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain); + } + return false; + } + function checkGrammarForOmittedArgument(args: NodeArray | undefined): boolean { if (args) { for (const arg of args) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b296c224d089d..93763804cf764 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1035,6 +1035,10 @@ "category": "Error", "code": 1356 }, + "Tagged template expressions are not permitted in an optional chain.": { + "category": "Error", + "code": 1357 + }, "Duplicate identifier '{0}'.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d427de81dd9f4..23479907c87f4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4630,7 +4630,7 @@ namespace ts { let isPropertyAccess = false; if (allowOptionalChain && isStartOfOptionalPropertyOrElementAccessChain()) { questionDotToken = parseExpectedToken(SyntaxKind.QuestionDotToken); - isPropertyAccess = token() !== SyntaxKind.OpenBracketToken; + isPropertyAccess = tokenIsIdentifierOrKeyword(token()); } else { isPropertyAccess = parseOptional(SyntaxKind.DotToken); diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index c2693fb1a4aaf..c51ad4119d297 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -23,7 +23,6 @@ namespace ts { case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: case SyntaxKind.CallExpression: - case SyntaxKind.TaggedTemplateExpression: if (node.flags & NodeFlags.OptionalChain) { const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false); Debug.assertNotNode(updated, isSyntheticReference); diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index db9e96e1ad2fc..a324993494db0 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -75,6 +75,7 @@ "unittests/evaluation/awaiter.ts", "unittests/evaluation/forAwaitOf.ts", "unittests/evaluation/forOf.ts", + "unittests/evaluation/optionalCall.ts", "unittests/evaluation/objectRest.ts", "unittests/services/cancellableLanguageServiceOperations.ts", "unittests/services/colorization.ts", diff --git a/src/testRunner/unittests/evaluation/optionalCall.ts b/src/testRunner/unittests/evaluation/optionalCall.ts new file mode 100644 index 0000000000000..d6317ad0a8ca9 --- /dev/null +++ b/src/testRunner/unittests/evaluation/optionalCall.ts @@ -0,0 +1,191 @@ +describe("unittests:: evaluation:: optionalCall", () => { + it("f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + function f(a) { + output.push(a); + output.push(this); + } + export const output: any[] = []; + f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.isUndefined(result.output[1]); + }); + it("o.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + export const output: any[] = []; + o.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o); + }); + it("o.x.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o.x.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + export const output: any[] = []; + o?.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o); + }); + it("o?.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + export const output: any[] = []; + o?.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o); + }); + it("o.x?.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o.x?.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.x.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o?.x.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.x?.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o?.x?.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.x?.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o?.x?.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("f?.()?.()", async () => { + const result = evaluator.evaluateTypeScript(` + function g(a) { + output.push(a); + output.push(this); + } + function f(a) { + output.push(a); + return g; + } + export const output: any[] = []; + f?.(1)?.(2) + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], 2); + assert.isUndefined(result.output[2]); + }); + it("f?.().f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + function f(a) { + output.push(a); + return o; + } + export const output: any[] = []; + f?.(1).f?.(2) + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], 2); + assert.strictEqual(result.output[2], result.o); + }); + it("f?.()?.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + function f(a) { + output.push(a); + return o; + } + export const output: any[] = []; + f?.(1)?.f?.(2) + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], 2); + assert.strictEqual(result.output[2], result.o); + }); +}); diff --git a/tests/baselines/reference/callChain.2.js b/tests/baselines/reference/callChain.2.js new file mode 100644 index 0000000000000..a44115ef91673 --- /dev/null +++ b/tests/baselines/reference/callChain.2.js @@ -0,0 +1,16 @@ +//// [callChain.2.ts] +declare const o1: undefined | (() => number); +o1?.(); + +declare const o2: undefined | { b: () => number }; +o2?.b(); + +declare const o3: { b: (() => { c: string }) | undefined }; +o3.b?.().c; + + +//// [callChain.2.js] +var _a, _b, _c, _d; +(_a = o1) === null || _a === void 0 ? void 0 : _a(); +(_b = o2) === null || _b === void 0 ? void 0 : _b.b(); +(_d = (_c = o3).b) === null || _d === void 0 ? void 0 : _d.call(_c).c; diff --git a/tests/baselines/reference/callChain.2.symbols b/tests/baselines/reference/callChain.2.symbols new file mode 100644 index 0000000000000..26c8859940e74 --- /dev/null +++ b/tests/baselines/reference/callChain.2.symbols @@ -0,0 +1,28 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts === +declare const o1: undefined | (() => number); +>o1 : Symbol(o1, Decl(callChain.2.ts, 0, 13)) + +o1?.(); +>o1 : Symbol(o1, Decl(callChain.2.ts, 0, 13)) + +declare const o2: undefined | { b: () => number }; +>o2 : Symbol(o2, Decl(callChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 3, 31)) + +o2?.b(); +>o2?.b : Symbol(b, Decl(callChain.2.ts, 3, 31)) +>o2 : Symbol(o2, Decl(callChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 3, 31)) + +declare const o3: { b: (() => { c: string }) | undefined }; +>o3 : Symbol(o3, Decl(callChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(callChain.2.ts, 6, 31)) + +o3.b?.().c; +>o3.b?.().c : Symbol(c, Decl(callChain.2.ts, 6, 31)) +>o3.b : Symbol(b, Decl(callChain.2.ts, 6, 19)) +>o3 : Symbol(o3, Decl(callChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(callChain.2.ts, 6, 31)) + diff --git a/tests/baselines/reference/callChain.2.types b/tests/baselines/reference/callChain.2.types new file mode 100644 index 0000000000000..53ea569cf8e59 --- /dev/null +++ b/tests/baselines/reference/callChain.2.types @@ -0,0 +1,31 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts === +declare const o1: undefined | (() => number); +>o1 : () => number + +o1?.(); +>o1?.() : number +>o1 : () => number + +declare const o2: undefined | { b: () => number }; +>o2 : { b: () => number; } +>b : () => number + +o2?.b(); +>o2?.b() : number +>o2?.b : () => number +>o2 : { b: () => number; } +>b : () => number + +declare const o3: { b: (() => { c: string }) | undefined }; +>o3 : { b: () => { c: string; }; } +>b : () => { c: string; } +>c : string + +o3.b?.().c; +>o3.b?.().c : string +>o3.b?.() : { c: string; } +>o3.b : () => { c: string; } +>o3 : { b: () => { c: string; }; } +>b : () => { c: string; } +>c : string + diff --git a/tests/baselines/reference/elementAccessChain.2.js b/tests/baselines/reference/elementAccessChain.2.js new file mode 100644 index 0000000000000..4212e5a20b110 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.2.js @@ -0,0 +1,20 @@ +//// [elementAccessChain.2.ts] +declare const o1: undefined | { b: string }; +o1?.["b"]; + +declare const o2: undefined | { b: { c: string } }; +o2?.["b"].c; +o2?.b["c"]; + +declare const o3: { b: undefined | { c: string } }; +o3["b"]?.c; +o3.b?.["c"]; + + +//// [elementAccessChain.2.js] +var _a, _b, _c, _d, _e; +(_a = o1) === null || _a === void 0 ? void 0 : _a["b"]; +(_b = o2) === null || _b === void 0 ? void 0 : _b["b"].c; +(_c = o2) === null || _c === void 0 ? void 0 : _c.b["c"]; +(_d = o3["b"]) === null || _d === void 0 ? void 0 : _d.c; +(_e = o3.b) === null || _e === void 0 ? void 0 : _e["c"]; diff --git a/tests/baselines/reference/elementAccessChain.2.symbols b/tests/baselines/reference/elementAccessChain.2.symbols new file mode 100644 index 0000000000000..689e8ec94cd87 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.2.symbols @@ -0,0 +1,43 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : Symbol(o1, Decl(elementAccessChain.2.ts, 0, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 0, 31)) + +o1?.["b"]; +>o1 : Symbol(o1, Decl(elementAccessChain.2.ts, 0, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.2.ts, 0, 31)) + +declare const o2: undefined | { b: { c: string } }; +>o2 : Symbol(o2, Decl(elementAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) + +o2?.["b"].c; +>o2?.["b"].c : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) +>o2 : Symbol(o2, Decl(elementAccessChain.2.ts, 3, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) + +o2?.b["c"]; +>o2?.b : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>o2 : Symbol(o2, Decl(elementAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>"c" : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) + +declare const o3: { b: undefined | { c: string } }; +>o3 : Symbol(o3, Decl(elementAccessChain.2.ts, 7, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) + +o3["b"]?.c; +>o3["b"]?.c : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) +>o3 : Symbol(o3, Decl(elementAccessChain.2.ts, 7, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) + +o3.b?.["c"]; +>o3.b : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>o3 : Symbol(o3, Decl(elementAccessChain.2.ts, 7, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>"c" : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) + diff --git a/tests/baselines/reference/elementAccessChain.2.types b/tests/baselines/reference/elementAccessChain.2.types new file mode 100644 index 0000000000000..ef6b56075246c --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.2.types @@ -0,0 +1,48 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : { b: string; } +>b : string + +o1?.["b"]; +>o1?.["b"] : string +>o1 : { b: string; } +>"b" : "b" + +declare const o2: undefined | { b: { c: string } }; +>o2 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o2?.["b"].c; +>o2?.["b"].c : string +>o2?.["b"] : { c: string; } +>o2 : { b: { c: string; }; } +>"b" : "b" +>c : string + +o2?.b["c"]; +>o2?.b["c"] : string +>o2?.b : { c: string; } +>o2 : { b: { c: string; }; } +>b : { c: string; } +>"c" : "c" + +declare const o3: { b: undefined | { c: string } }; +>o3 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o3["b"]?.c; +>o3["b"]?.c : string +>o3["b"] : { c: string; } +>o3 : { b: { c: string; }; } +>"b" : "b" +>c : string + +o3.b?.["c"]; +>o3.b?.["c"] : string +>o3.b : { c: string; } +>o3 : { b: { c: string; }; } +>b : { c: string; } +>"c" : "c" + diff --git a/tests/baselines/reference/propertyAccessChain.2.js b/tests/baselines/reference/propertyAccessChain.2.js new file mode 100644 index 0000000000000..939e9830b549f --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.2.js @@ -0,0 +1,16 @@ +//// [propertyAccessChain.2.ts] +declare const o1: undefined | { b: string }; +o1?.b; + +declare const o2: undefined | { b: { c: string } }; +o2?.b.c; + +declare const o3: { b: undefined | { c: string } }; +o3.b?.c; + + +//// [propertyAccessChain.2.js] +var _a, _b, _c; +(_a = o1) === null || _a === void 0 ? void 0 : _a.b; +(_b = o2) === null || _b === void 0 ? void 0 : _b.b.c; +(_c = o3.b) === null || _c === void 0 ? void 0 : _c.c; diff --git a/tests/baselines/reference/propertyAccessChain.2.symbols b/tests/baselines/reference/propertyAccessChain.2.symbols new file mode 100644 index 0000000000000..2d4fd96896696 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.2.symbols @@ -0,0 +1,34 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : Symbol(o1, Decl(propertyAccessChain.2.ts, 0, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 0, 31)) + +o1?.b; +>o1?.b : Symbol(b, Decl(propertyAccessChain.2.ts, 0, 31)) +>o1 : Symbol(o1, Decl(propertyAccessChain.2.ts, 0, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 0, 31)) + +declare const o2: undefined | { b: { c: string } }; +>o2 : Symbol(o2, Decl(propertyAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 3, 36)) + +o2?.b.c; +>o2?.b.c : Symbol(c, Decl(propertyAccessChain.2.ts, 3, 36)) +>o2?.b : Symbol(b, Decl(propertyAccessChain.2.ts, 3, 31)) +>o2 : Symbol(o2, Decl(propertyAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 3, 36)) + +declare const o3: { b: undefined | { c: string } }; +>o3 : Symbol(o3, Decl(propertyAccessChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 6, 36)) + +o3.b?.c; +>o3.b?.c : Symbol(c, Decl(propertyAccessChain.2.ts, 6, 36)) +>o3.b : Symbol(b, Decl(propertyAccessChain.2.ts, 6, 19)) +>o3 : Symbol(o3, Decl(propertyAccessChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 6, 36)) + diff --git a/tests/baselines/reference/propertyAccessChain.2.types b/tests/baselines/reference/propertyAccessChain.2.types new file mode 100644 index 0000000000000..6a046048f15a9 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.2.types @@ -0,0 +1,34 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : { b: string; } +>b : string + +o1?.b; +>o1?.b : string +>o1 : { b: string; } +>b : string + +declare const o2: undefined | { b: { c: string } }; +>o2 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o2?.b.c; +>o2?.b.c : string +>o2?.b : { c: string; } +>o2 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +declare const o3: { b: undefined | { c: string } }; +>o3 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o3.b?.c; +>o3.b?.c : string +>o3.b : { c: string; } +>o3 : { b: { c: string; }; } +>b : { c: string; } +>c : string + diff --git a/tests/baselines/reference/taggedTemplateChain.errors.txt b/tests/baselines/reference/taggedTemplateChain.errors.txt new file mode 100644 index 0000000000000..dc796115ac887 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.errors.txt @@ -0,0 +1,13 @@ +tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts(2,4): error TS1357: Tagged template expressions are not permitted in an optional chain. +tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts(4,4): error TS1357: Tagged template expressions are not permitted in an optional chain. + + +==== tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts (2 errors) ==== + declare let a: any; + a?.`b`; + ~~~ +!!! error TS1357: Tagged template expressions are not permitted in an optional chain. + + a?.`b${1}c`; + ~~~~~~~~ +!!! error TS1357: Tagged template expressions are not permitted in an optional chain. \ No newline at end of file diff --git a/tests/baselines/reference/taggedTemplateChain.js b/tests/baselines/reference/taggedTemplateChain.js new file mode 100644 index 0000000000000..c96237185fb45 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.js @@ -0,0 +1,13 @@ +//// [taggedTemplateChain.ts] +declare let a: any; +a?.`b`; + +a?.`b${1}c`; + +//// [taggedTemplateChain.js] +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +a(__makeTemplateObject(["b"], ["b"])); +a(__makeTemplateObject(["b", "c"], ["b", "c"]), 1); diff --git a/tests/baselines/reference/taggedTemplateChain.symbols b/tests/baselines/reference/taggedTemplateChain.symbols new file mode 100644 index 0000000000000..4dde630cab6ac --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.symbols @@ -0,0 +1,10 @@ +=== tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts === +declare let a: any; +>a : Symbol(a, Decl(taggedTemplateChain.ts, 0, 11)) + +a?.`b`; +>a : Symbol(a, Decl(taggedTemplateChain.ts, 0, 11)) + +a?.`b${1}c`; +>a : Symbol(a, Decl(taggedTemplateChain.ts, 0, 11)) + diff --git a/tests/baselines/reference/taggedTemplateChain.types b/tests/baselines/reference/taggedTemplateChain.types new file mode 100644 index 0000000000000..9690fc22f6991 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.types @@ -0,0 +1,15 @@ +=== tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts === +declare let a: any; +>a : any + +a?.`b`; +>a?.`b` : any +>a : any +>`b` : "b" + +a?.`b${1}c`; +>a?.`b${1}c` : any +>a : any +>`b${1}c` : string +>1 : 1 + diff --git a/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts new file mode 100644 index 0000000000000..58e51858da6a1 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts @@ -0,0 +1,10 @@ +// @strict: false + +declare const o1: undefined | (() => number); +o1?.(); + +declare const o2: undefined | { b: () => number }; +o2?.b(); + +declare const o3: { b: (() => { c: string }) | undefined }; +o3.b?.().c; diff --git a/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts new file mode 100644 index 0000000000000..00834b4d968a0 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts @@ -0,0 +1,12 @@ +// @strict: false + +declare const o1: undefined | { b: string }; +o1?.["b"]; + +declare const o2: undefined | { b: { c: string } }; +o2?.["b"].c; +o2?.b["c"]; + +declare const o3: { b: undefined | { c: string } }; +o3["b"]?.c; +o3.b?.["c"]; diff --git a/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts new file mode 100644 index 0000000000000..93e37f0b48f64 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts @@ -0,0 +1,10 @@ +// @strict: false + +declare const o1: undefined | { b: string }; +o1?.b; + +declare const o2: undefined | { b: { c: string } }; +o2?.b.c; + +declare const o3: { b: undefined | { c: string } }; +o3.b?.c; diff --git a/tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts b/tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts new file mode 100644 index 0000000000000..6a3bb04336e63 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts @@ -0,0 +1,4 @@ +declare let a: any; +a?.`b`; + +a?.`b${1}c`; \ No newline at end of file