From 4843a2aa45f541251b9b0844a8c70470afa90040 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 22 Sep 2020 07:27:12 -1000 Subject: [PATCH 1/3] Support const assertions with template literal expressions --- src/compiler/checker.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a9d11787a14a3..947ecc6e1ffaf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28212,6 +28212,7 @@ namespace ts { case SyntaxKind.FalseKeyword: case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.TemplateExpression: return true; case SyntaxKind.ParenthesizedExpression: return isValidConstAssertionArgument((node).expression); @@ -30284,18 +30285,17 @@ namespace ts { } function checkTemplateExpression(node: TemplateExpression): Type { - // We just want to check each expressions, but we are unconcerned with - // the type of each expression, as any value may be coerced into a string. - // It is worth asking whether this is what we really want though. - // A place where we actually *are* concerned with the expressions' types are - // in tagged templates. - forEach(node.templateSpans, templateSpan => { - if (maybeTypeOfKind(checkExpression(templateSpan.expression), TypeFlags.ESSymbolLike)) { - error(templateSpan.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String); + const texts = [node.head.text]; + const types = []; + for (const span of node.templateSpans) { + const type = checkExpression(span.expression); + if (maybeTypeOfKind(type, TypeFlags.ESSymbolLike)) { + error(span.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String); } - }); - - return stringType; + texts.push(span.literal.text); + types.push(isTypeAssignableTo(type, templateConstraintType) ? type : stringType); + } + return isConstContext(node) ? getTemplateLiteralType(texts, types) : stringType; } function getContextNode(node: Expression): Node { @@ -30427,7 +30427,7 @@ namespace ts { const parent = node.parent; return isAssertionExpression(parent) && isConstTypeReference(parent.type) || (isParenthesizedExpression(parent) || isArrayLiteralExpression(parent) || isSpreadElement(parent)) && isConstContext(parent) || - (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent)) && isConstContext(parent.parent); + (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent); } function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type { From 9145146c89df800c3abf57949ff35b20473a2f24 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 22 Sep 2020 08:58:46 -1000 Subject: [PATCH 2/3] Add tests --- .../typeAssertions/constAssertions.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/cases/conformance/expressions/typeAssertions/constAssertions.ts b/tests/cases/conformance/expressions/typeAssertions/constAssertions.ts index e2ce7aba9935c..44428174b2a7a 100644 --- a/tests/cases/conformance/expressions/typeAssertions/constAssertions.ts +++ b/tests/cases/conformance/expressions/typeAssertions/constAssertions.ts @@ -65,3 +65,48 @@ declare function id(x: T): T; let e1 = v1 as const; // Error let e2 = (true ? 1 : 0) as const; // Error let e3 = id(1) as const; // Error + +let t1 = 'foo' as const; +let t2 = 'bar' as const; +let t3 = `${t1}-${t2}` as const; +let t4 = `${`(${t1})`}-${`(${t2})`}` as const; + +function ff1(x: 'foo' | 'bar', y: 1 | 2) { + return `${x}-${y}` as const; +} + +function ff2(x: T, y: U) { + return `${x}-${y}` as const; +} + +const ts1 = ff2('foo', 'bar'); +const ts2 = ff2('foo', !!true ? '0' : '1'); +const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right'); + +function ff3(x: 'foo' | 'bar', y: object) { + return `${x}${y}` as const; +} + +type Action = "verify" | "write"; +type ContentMatch = "match" | "nonMatch"; +type Outcome = `${Action}_${ContentMatch}`; + +function ff4(verify: boolean, contentMatches: boolean) { + const action : Action = verify ? `verify` : `write`; + const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`; + const outcome: Outcome = `${action}_${contentMatch}` as const; + return outcome; +} + +function ff5(verify: boolean, contentMatches: boolean) { + const action = verify ? `verify` : `write`; + const contentMatch = contentMatches ? `match` : `nonMatch`; + const outcome = `${action}_${contentMatch}` as const; + return outcome; +} + +function accessorNames(propName: S) { + return [`get-${propName}`, `set-${propName}`] as const; +} + +const ns1 = accessorNames('foo'); \ No newline at end of file From e86330578dfa51e842bb3ff9dd4807c14834001f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 22 Sep 2020 08:58:53 -1000 Subject: [PATCH 3/3] Accept new baselines --- .../reference/constAssertions.errors.txt | 46 ++++- tests/baselines/reference/constAssertions.js | 95 ++++++++- .../reference/constAssertions.symbols | 135 +++++++++++++ .../baselines/reference/constAssertions.types | 186 ++++++++++++++++++ 4 files changed, 460 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/constAssertions.errors.txt b/tests/baselines/reference/constAssertions.errors.txt index 8ecce1377e344..ef237f5017c6b 100644 --- a/tests/baselines/reference/constAssertions.errors.txt +++ b/tests/baselines/reference/constAssertions.errors.txt @@ -76,4 +76,48 @@ tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(63,10): er let e3 = id(1) as const; // Error ~~~~~ !!! error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. - \ No newline at end of file + + let t1 = 'foo' as const; + let t2 = 'bar' as const; + let t3 = `${t1}-${t2}` as const; + let t4 = `${`(${t1})`}-${`(${t2})`}` as const; + + function ff1(x: 'foo' | 'bar', y: 1 | 2) { + return `${x}-${y}` as const; + } + + function ff2(x: T, y: U) { + return `${x}-${y}` as const; + } + + const ts1 = ff2('foo', 'bar'); + const ts2 = ff2('foo', !!true ? '0' : '1'); + const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right'); + + function ff3(x: 'foo' | 'bar', y: object) { + return `${x}${y}` as const; + } + + type Action = "verify" | "write"; + type ContentMatch = "match" | "nonMatch"; + type Outcome = `${Action}_${ContentMatch}`; + + function ff4(verify: boolean, contentMatches: boolean) { + const action : Action = verify ? `verify` : `write`; + const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`; + const outcome: Outcome = `${action}_${contentMatch}` as const; + return outcome; + } + + function ff5(verify: boolean, contentMatches: boolean) { + const action = verify ? `verify` : `write`; + const contentMatch = contentMatches ? `match` : `nonMatch`; + const outcome = `${action}_${contentMatch}` as const; + return outcome; + } + + function accessorNames(propName: S) { + return [`get-${propName}`, `set-${propName}`] as const; + } + + const ns1 = accessorNames('foo'); \ No newline at end of file diff --git a/tests/baselines/reference/constAssertions.js b/tests/baselines/reference/constAssertions.js index 1dc997a2c5819..238d436adaf5f 100644 --- a/tests/baselines/reference/constAssertions.js +++ b/tests/baselines/reference/constAssertions.js @@ -62,7 +62,51 @@ declare function id(x: T): T; let e1 = v1 as const; // Error let e2 = (true ? 1 : 0) as const; // Error let e3 = id(1) as const; // Error - + +let t1 = 'foo' as const; +let t2 = 'bar' as const; +let t3 = `${t1}-${t2}` as const; +let t4 = `${`(${t1})`}-${`(${t2})`}` as const; + +function ff1(x: 'foo' | 'bar', y: 1 | 2) { + return `${x}-${y}` as const; +} + +function ff2(x: T, y: U) { + return `${x}-${y}` as const; +} + +const ts1 = ff2('foo', 'bar'); +const ts2 = ff2('foo', !!true ? '0' : '1'); +const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right'); + +function ff3(x: 'foo' | 'bar', y: object) { + return `${x}${y}` as const; +} + +type Action = "verify" | "write"; +type ContentMatch = "match" | "nonMatch"; +type Outcome = `${Action}_${ContentMatch}`; + +function ff4(verify: boolean, contentMatches: boolean) { + const action : Action = verify ? `verify` : `write`; + const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`; + const outcome: Outcome = `${action}_${contentMatch}` as const; + return outcome; +} + +function ff5(verify: boolean, contentMatches: boolean) { + const action = verify ? `verify` : `write`; + const contentMatch = contentMatches ? `match` : `nonMatch`; + const outcome = `${action}_${contentMatch}` as const; + return outcome; +} + +function accessorNames(propName: S) { + return [`get-${propName}`, `set-${propName}`] as const; +} + +const ns1 = accessorNames('foo'); //// [constAssertions.js] "use strict"; @@ -117,6 +161,38 @@ let q5 = { x: 10, y: 20 }; let e1 = v1; // Error let e2 = (true ? 1 : 0); // Error let e3 = id(1); // Error +let t1 = 'foo'; +let t2 = 'bar'; +let t3 = `${t1}-${t2}`; +let t4 = `${`(${t1})`}-${`(${t2})`}`; +function ff1(x, y) { + return `${x}-${y}`; +} +function ff2(x, y) { + return `${x}-${y}`; +} +const ts1 = ff2('foo', 'bar'); +const ts2 = ff2('foo', !!true ? '0' : '1'); +const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right'); +function ff3(x, y) { + return `${x}${y}`; +} +function ff4(verify, contentMatches) { + const action = verify ? `verify` : `write`; + const contentMatch = contentMatches ? `match` : `nonMatch`; + const outcome = `${action}_${contentMatch}`; + return outcome; +} +function ff5(verify, contentMatches) { + const action = verify ? `verify` : `write`; + const contentMatch = contentMatches ? `match` : `nonMatch`; + const outcome = `${action}_${contentMatch}`; + return outcome; +} +function accessorNames(propName) { + return [`get-${propName}`, `set-${propName}`]; +} +const ns1 = accessorNames('foo'); //// [constAssertions.d.ts] @@ -218,3 +294,20 @@ declare function id(x: T): T; declare let e1: "abc"; declare let e2: 0 | 1; declare let e3: 1; +declare let t1: "foo"; +declare let t2: "bar"; +declare let t3: "foo-bar"; +declare let t4: "(foo)-(bar)"; +declare function ff1(x: 'foo' | 'bar', y: 1 | 2): "foo-1" | "foo-2" | "bar-1" | "bar-2"; +declare function ff2(x: T, y: U): `${T}-${U}`; +declare const ts1: "foo-bar"; +declare const ts2: "foo-1" | "foo-0"; +declare const ts3: "top-left" | "top-right" | "bottom-left" | "bottom-right"; +declare function ff3(x: 'foo' | 'bar', y: object): string; +declare type Action = "verify" | "write"; +declare type ContentMatch = "match" | "nonMatch"; +declare type Outcome = `${Action}_${ContentMatch}`; +declare function ff4(verify: boolean, contentMatches: boolean): "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"; +declare function ff5(verify: boolean, contentMatches: boolean): "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"; +declare function accessorNames(propName: S): readonly [`get-${S}`, `set-${S}`]; +declare const ns1: readonly ["get-foo", "set-foo"]; diff --git a/tests/baselines/reference/constAssertions.symbols b/tests/baselines/reference/constAssertions.symbols index 598210f42b396..6e81b68fa87ef 100644 --- a/tests/baselines/reference/constAssertions.symbols +++ b/tests/baselines/reference/constAssertions.symbols @@ -199,3 +199,138 @@ let e3 = id(1) as const; // Error >e3 : Symbol(e3, Decl(constAssertions.ts, 62, 3)) >id : Symbol(id, Decl(constAssertions.ts, 56, 34)) +let t1 = 'foo' as const; +>t1 : Symbol(t1, Decl(constAssertions.ts, 64, 3)) + +let t2 = 'bar' as const; +>t2 : Symbol(t2, Decl(constAssertions.ts, 65, 3)) + +let t3 = `${t1}-${t2}` as const; +>t3 : Symbol(t3, Decl(constAssertions.ts, 66, 3)) +>t1 : Symbol(t1, Decl(constAssertions.ts, 64, 3)) +>t2 : Symbol(t2, Decl(constAssertions.ts, 65, 3)) + +let t4 = `${`(${t1})`}-${`(${t2})`}` as const; +>t4 : Symbol(t4, Decl(constAssertions.ts, 67, 3)) +>t1 : Symbol(t1, Decl(constAssertions.ts, 64, 3)) +>t2 : Symbol(t2, Decl(constAssertions.ts, 65, 3)) + +function ff1(x: 'foo' | 'bar', y: 1 | 2) { +>ff1 : Symbol(ff1, Decl(constAssertions.ts, 67, 46)) +>x : Symbol(x, Decl(constAssertions.ts, 69, 13)) +>y : Symbol(y, Decl(constAssertions.ts, 69, 30)) + + return `${x}-${y}` as const; +>x : Symbol(x, Decl(constAssertions.ts, 69, 13)) +>y : Symbol(y, Decl(constAssertions.ts, 69, 30)) +} + +function ff2(x: T, y: U) { +>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1)) +>T : Symbol(T, Decl(constAssertions.ts, 73, 13)) +>U : Symbol(U, Decl(constAssertions.ts, 73, 30)) +>x : Symbol(x, Decl(constAssertions.ts, 73, 49)) +>T : Symbol(T, Decl(constAssertions.ts, 73, 13)) +>y : Symbol(y, Decl(constAssertions.ts, 73, 54)) +>U : Symbol(U, Decl(constAssertions.ts, 73, 30)) + + return `${x}-${y}` as const; +>x : Symbol(x, Decl(constAssertions.ts, 73, 49)) +>y : Symbol(y, Decl(constAssertions.ts, 73, 54)) +} + +const ts1 = ff2('foo', 'bar'); +>ts1 : Symbol(ts1, Decl(constAssertions.ts, 77, 5)) +>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1)) + +const ts2 = ff2('foo', !!true ? '0' : '1'); +>ts2 : Symbol(ts2, Decl(constAssertions.ts, 78, 5)) +>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1)) + +const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right'); +>ts3 : Symbol(ts3, Decl(constAssertions.ts, 79, 5)) +>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1)) + +function ff3(x: 'foo' | 'bar', y: object) { +>ff3 : Symbol(ff3, Decl(constAssertions.ts, 79, 70)) +>x : Symbol(x, Decl(constAssertions.ts, 81, 13)) +>y : Symbol(y, Decl(constAssertions.ts, 81, 30)) + + return `${x}${y}` as const; +>x : Symbol(x, Decl(constAssertions.ts, 81, 13)) +>y : Symbol(y, Decl(constAssertions.ts, 81, 30)) +} + +type Action = "verify" | "write"; +>Action : Symbol(Action, Decl(constAssertions.ts, 83, 1)) + +type ContentMatch = "match" | "nonMatch"; +>ContentMatch : Symbol(ContentMatch, Decl(constAssertions.ts, 85, 33)) + +type Outcome = `${Action}_${ContentMatch}`; +>Outcome : Symbol(Outcome, Decl(constAssertions.ts, 86, 41)) +>Action : Symbol(Action, Decl(constAssertions.ts, 83, 1)) +>ContentMatch : Symbol(ContentMatch, Decl(constAssertions.ts, 85, 33)) + +function ff4(verify: boolean, contentMatches: boolean) { +>ff4 : Symbol(ff4, Decl(constAssertions.ts, 87, 43)) +>verify : Symbol(verify, Decl(constAssertions.ts, 89, 13)) +>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 89, 29)) + + const action : Action = verify ? `verify` : `write`; +>action : Symbol(action, Decl(constAssertions.ts, 90, 9)) +>Action : Symbol(Action, Decl(constAssertions.ts, 83, 1)) +>verify : Symbol(verify, Decl(constAssertions.ts, 89, 13)) + + const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`; +>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 91, 9)) +>ContentMatch : Symbol(ContentMatch, Decl(constAssertions.ts, 85, 33)) +>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 89, 29)) + + const outcome: Outcome = `${action}_${contentMatch}` as const; +>outcome : Symbol(outcome, Decl(constAssertions.ts, 92, 9)) +>Outcome : Symbol(Outcome, Decl(constAssertions.ts, 86, 41)) +>action : Symbol(action, Decl(constAssertions.ts, 90, 9)) +>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 91, 9)) + + return outcome; +>outcome : Symbol(outcome, Decl(constAssertions.ts, 92, 9)) +} + +function ff5(verify: boolean, contentMatches: boolean) { +>ff5 : Symbol(ff5, Decl(constAssertions.ts, 94, 1)) +>verify : Symbol(verify, Decl(constAssertions.ts, 96, 13)) +>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 96, 29)) + + const action = verify ? `verify` : `write`; +>action : Symbol(action, Decl(constAssertions.ts, 97, 9)) +>verify : Symbol(verify, Decl(constAssertions.ts, 96, 13)) + + const contentMatch = contentMatches ? `match` : `nonMatch`; +>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 98, 9)) +>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 96, 29)) + + const outcome = `${action}_${contentMatch}` as const; +>outcome : Symbol(outcome, Decl(constAssertions.ts, 99, 9)) +>action : Symbol(action, Decl(constAssertions.ts, 97, 9)) +>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 98, 9)) + + return outcome; +>outcome : Symbol(outcome, Decl(constAssertions.ts, 99, 9)) +} + +function accessorNames(propName: S) { +>accessorNames : Symbol(accessorNames, Decl(constAssertions.ts, 101, 1)) +>S : Symbol(S, Decl(constAssertions.ts, 103, 23)) +>propName : Symbol(propName, Decl(constAssertions.ts, 103, 41)) +>S : Symbol(S, Decl(constAssertions.ts, 103, 23)) + + return [`get-${propName}`, `set-${propName}`] as const; +>propName : Symbol(propName, Decl(constAssertions.ts, 103, 41)) +>propName : Symbol(propName, Decl(constAssertions.ts, 103, 41)) +} + +const ns1 = accessorNames('foo'); +>ns1 : Symbol(ns1, Decl(constAssertions.ts, 107, 5)) +>accessorNames : Symbol(accessorNames, Decl(constAssertions.ts, 101, 1)) + diff --git a/tests/baselines/reference/constAssertions.types b/tests/baselines/reference/constAssertions.types index 68b442e0c4456..cdbebebd392aa 100644 --- a/tests/baselines/reference/constAssertions.types +++ b/tests/baselines/reference/constAssertions.types @@ -354,3 +354,189 @@ let e3 = id(1) as const; // Error >id : (x: T) => T >1 : 1 +let t1 = 'foo' as const; +>t1 : "foo" +>'foo' as const : "foo" +>'foo' : "foo" + +let t2 = 'bar' as const; +>t2 : "bar" +>'bar' as const : "bar" +>'bar' : "bar" + +let t3 = `${t1}-${t2}` as const; +>t3 : "foo-bar" +>`${t1}-${t2}` as const : "foo-bar" +>`${t1}-${t2}` : "foo-bar" +>t1 : "foo" +>t2 : "bar" + +let t4 = `${`(${t1})`}-${`(${t2})`}` as const; +>t4 : "(foo)-(bar)" +>`${`(${t1})`}-${`(${t2})`}` as const : "(foo)-(bar)" +>`${`(${t1})`}-${`(${t2})`}` : "(foo)-(bar)" +>`(${t1})` : "(foo)" +>t1 : "foo" +>`(${t2})` : "(bar)" +>t2 : "bar" + +function ff1(x: 'foo' | 'bar', y: 1 | 2) { +>ff1 : (x: 'foo' | 'bar', y: 1 | 2) => "foo-1" | "foo-2" | "bar-1" | "bar-2" +>x : "foo" | "bar" +>y : 1 | 2 + + return `${x}-${y}` as const; +>`${x}-${y}` as const : "foo-1" | "foo-2" | "bar-1" | "bar-2" +>`${x}-${y}` : "foo-1" | "foo-2" | "bar-1" | "bar-2" +>x : "foo" | "bar" +>y : 1 | 2 +} + +function ff2(x: T, y: U) { +>ff2 : (x: T, y: U) => `${T}-${U}` +>x : T +>y : U + + return `${x}-${y}` as const; +>`${x}-${y}` as const : `${T}-${U}` +>`${x}-${y}` : `${T}-${U}` +>x : T +>y : U +} + +const ts1 = ff2('foo', 'bar'); +>ts1 : "foo-bar" +>ff2('foo', 'bar') : "foo-bar" +>ff2 : (x: T, y: U) => `${T}-${U}` +>'foo' : "foo" +>'bar' : "bar" + +const ts2 = ff2('foo', !!true ? '0' : '1'); +>ts2 : "foo-1" | "foo-0" +>ff2('foo', !!true ? '0' : '1') : "foo-1" | "foo-0" +>ff2 : (x: T, y: U) => `${T}-${U}` +>'foo' : "foo" +>!!true ? '0' : '1' : "0" | "1" +>!!true : true +>!true : false +>true : true +>'0' : "0" +>'1' : "1" + +const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right'); +>ts3 : "top-left" | "top-right" | "bottom-left" | "bottom-right" +>ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right') : "top-left" | "top-right" | "bottom-left" | "bottom-right" +>ff2 : (x: T, y: U) => `${T}-${U}` +>!!true ? 'top' : 'bottom' : "top" | "bottom" +>!!true : true +>!true : false +>true : true +>'top' : "top" +>'bottom' : "bottom" +>!!true ? 'left' : 'right' : "left" | "right" +>!!true : true +>!true : false +>true : true +>'left' : "left" +>'right' : "right" + +function ff3(x: 'foo' | 'bar', y: object) { +>ff3 : (x: 'foo' | 'bar', y: object) => string +>x : "foo" | "bar" +>y : object + + return `${x}${y}` as const; +>`${x}${y}` as const : string +>`${x}${y}` : string +>x : "foo" | "bar" +>y : object +} + +type Action = "verify" | "write"; +>Action : Action + +type ContentMatch = "match" | "nonMatch"; +>ContentMatch : ContentMatch + +type Outcome = `${Action}_${ContentMatch}`; +>Outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" + +function ff4(verify: boolean, contentMatches: boolean) { +>ff4 : (verify: boolean, contentMatches: boolean) => "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>verify : boolean +>contentMatches : boolean + + const action : Action = verify ? `verify` : `write`; +>action : Action +>verify ? `verify` : `write` : Action +>verify : boolean +>`verify` : "verify" +>`write` : "write" + + const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`; +>contentMatch : ContentMatch +>contentMatches ? `match` : `nonMatch` : ContentMatch +>contentMatches : boolean +>`match` : "match" +>`nonMatch` : "nonMatch" + + const outcome: Outcome = `${action}_${contentMatch}` as const; +>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>`${action}_${contentMatch}` as const : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>`${action}_${contentMatch}` : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>action : Action +>contentMatch : ContentMatch + + return outcome; +>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +} + +function ff5(verify: boolean, contentMatches: boolean) { +>ff5 : (verify: boolean, contentMatches: boolean) => "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>verify : boolean +>contentMatches : boolean + + const action = verify ? `verify` : `write`; +>action : "verify" | "write" +>verify ? `verify` : `write` : Action +>verify : boolean +>`verify` : "verify" +>`write` : "write" + + const contentMatch = contentMatches ? `match` : `nonMatch`; +>contentMatch : "match" | "nonMatch" +>contentMatches ? `match` : `nonMatch` : ContentMatch +>contentMatches : boolean +>`match` : "match" +>`nonMatch` : "nonMatch" + + const outcome = `${action}_${contentMatch}` as const; +>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>`${action}_${contentMatch}` as const : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>`${action}_${contentMatch}` : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +>action : Action +>contentMatch : ContentMatch + + return outcome; +>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch" +} + +function accessorNames(propName: S) { +>accessorNames : (propName: S) => readonly [`get-${S}`, `set-${S}`] +>propName : S + + return [`get-${propName}`, `set-${propName}`] as const; +>[`get-${propName}`, `set-${propName}`] as const : readonly [`get-${S}`, `set-${S}`] +>[`get-${propName}`, `set-${propName}`] : readonly [`get-${S}`, `set-${S}`] +>`get-${propName}` : `get-${S}` +>propName : S +>`set-${propName}` : `set-${S}` +>propName : S +} + +const ns1 = accessorNames('foo'); +>ns1 : readonly ["get-foo", "set-foo"] +>accessorNames('foo') : readonly ["get-foo", "set-foo"] +>accessorNames : (propName: S) => readonly [`get-${S}`, `set-${S}`] +>'foo' : "foo" +