Repository: danvk/literate-ts
Pull request #9 (2020-06-29 17:38:46)

Require multiline type assertions to be aligned
There were some errors that popped up in _Effective TypeScript_ after #8. They looked like this:

```ts
getRanges() {
  for (const r of this.indices) {
    const low = r[0];  // Type is any
    const high = r[1];  // Type is any
    // ...
  }
}
```


Patch:
diff --git a/src/ts-checker.ts b/src/ts-checker.ts
index 15d1947..4ef64fb 100644
--- a/src/ts-checker.ts
+++ b/src/ts-checker.ts
@@ -149,7 +149,7 @@ export function extractTypeAssertions(
   const assertions = [] as TypeScriptTypeAssertion[];
 
   let appliesToPreviousLine = false;
-  let maybeContinuation = false;
+  let colForContinuation = null;
   while (scanner.scan() !== ts.SyntaxKind.EndOfFileToken) {
     const token = scanner.getToken();
     if (token === ts.SyntaxKind.WhitespaceTrivia) continue; // ignore leading whitespace.
@@ -161,11 +161,13 @@ export function extractTypeAssertions(
     }
 
     const pos = scanner.getTokenPos();
-    let {line} = source.getLineAndCharacterOfPosition(pos);
+    const lineChar = source.getLineAndCharacterOfPosition(pos);
+    let {line} = lineChar;
+    const {character} = lineChar;
 
     if (token === ts.SyntaxKind.SingleLineCommentTrivia) {
       const commentText = scanner.getTokenText();
-      if (maybeContinuation) {
+      if (character === colForContinuation) {
         assertions[assertions.length - 1].type += ' ' + commentText.slice(2).trim();
       } else {
         const type = matchAndExtract(TYPE_ASSERTION_PAT, commentText);
@@ -173,11 +175,11 @@ export function extractTypeAssertions(
 
         if (appliesToPreviousLine) line -= 1;
         assertions.push({line, type});
-        maybeContinuation = true;
+        colForContinuation = character;
       }
     } else {
       appliesToPreviousLine = false;
-      maybeContinuation = false;
+      colForContinuation = null;
     }
   }
   return assertions;


Test patch:
diff --git a/src/test/__snapshots__/asciidoc.test.ts.snap b/src/test/__snapshots__/asciidoc.test.ts.snap
index ea90250..36cda70 100644
--- a/src/test/__snapshots__/asciidoc.test.ts.snap
+++ b/src/test/__snapshots__/asciidoc.test.ts.snap
@@ -20,6 +20,8 @@ Array [
 ]
 `;
 
+exports[`extractSamples snapshot: multilinetype 1`] = `Array []`;
+
 exports[`extractSamples snapshot: noid 1`] = `
 Array [
   Object {
diff --git a/src/test/__snapshots__/markdown.test.ts.snap b/src/test/__snapshots__/markdown.test.ts.snap
index 3cfb7f5..fb5b734 100644
--- a/src/test/__snapshots__/markdown.test.ts.snap
+++ b/src/test/__snapshots__/markdown.test.ts.snap
@@ -20,6 +20,45 @@ Array [
 ]
 `;
 
+exports[`markdown should match snapshots: multilinetype 1`] = `
+Array [
+  Object {
+    "checkJS": false,
+    "content": "const o = {x: 1, y: 2};
+// type is {
+//   x: number;
+//   y: number;
+// }",
+    "id": "multilinetype-3",
+    "isTSX": false,
+    "language": "ts",
+    "nodeModules": Array [],
+    "prefixes": Array [],
+    "sectionHeader": null,
+    "sectionId": null,
+    "sourceFile": "multilinetype.md",
+    "tsOptions": Object {},
+  },
+  Object {
+    "checkJS": false,
+    "content": "function addWithExtras(a: number, b: number) {
+  const c = a + b;  // type is number
+  // ...
+  return c;
+}",
+    "id": "multilinetype-13",
+    "isTSX": false,
+    "language": "ts",
+    "nodeModules": Array [],
+    "prefixes": Array [],
+    "sectionHeader": null,
+    "sectionId": null,
+    "sourceFile": "multilinetype.md",
+    "tsOptions": Object {},
+  },
+]
+`;
+
 exports[`markdown should match snapshots: noid 1`] = `
 Array [
   Object {
diff --git a/src/test/asciidoc.test.ts b/src/test/asciidoc.test.ts
index 4cda570..e523632 100644
--- a/src/test/asciidoc.test.ts
+++ b/src/test/asciidoc.test.ts
@@ -80,7 +80,7 @@ describe('extractSamples', () => {
     const dir = './src/test/inputs';
 
     // TODO(danvk): use a glob here
-    const inputs = ['doc1', 'noid', 'prepend', 'prepend-multiple', 'skip'];
+    const inputs = ['doc1', 'noid', 'prepend', 'prepend-multiple', 'skip', 'multilinetype'];
 
     for (const input of inputs) {
       expect(
diff --git a/src/test/inputs/multilinetype.asciidoc b/src/test/inputs/multilinetype.asciidoc
new file mode 100644
index 0000000..3dc1cfd
--- /dev/null
+++ b/src/test/inputs/multilinetype.asciidoc
@@ -0,0 +1,19 @@
+This example has a multiline type assertion:
+
+```ts
+const o = {x: 1, y: 2};
+// type is {
+//   x: number;
+//   y: number;
+// }
+```
+
+Whereas this is not a multiline assertion:
+
+```ts
+function addWithExtras(a: number, b: number) {
+  const c = a + b;  // type is number
+  // ...
+  return c;
+}
+```
diff --git a/src/test/inputs/multilinetype.md b/src/test/inputs/multilinetype.md
new file mode 100644
index 0000000..3dc1cfd
--- /dev/null
+++ b/src/test/inputs/multilinetype.md
@@ -0,0 +1,19 @@
+This example has a multiline type assertion:
+
+```ts
+const o = {x: 1, y: 2};
+// type is {
+//   x: number;
+//   y: number;
+// }
+```
+
+Whereas this is not a multiline assertion:
+
+```ts
+function addWithExtras(a: number, b: number) {
+  const c = a + b;  // type is number
+  // ...
+  return c;
+}
+```
diff --git a/src/test/markdown.test.ts b/src/test/markdown.test.ts
index 4cca472..8fce2e9 100644
--- a/src/test/markdown.test.ts
+++ b/src/test/markdown.test.ts
@@ -7,7 +7,7 @@ describe('markdown', () => {
     const dir = './src/test/inputs';
 
     // TODO(danvk): use a glob here
-    const inputs = ['doc1', 'noid', 'prepend', 'prepend-multiple', 'skip'];
+    const inputs = ['doc1', 'noid', 'prepend', 'prepend-multiple', 'skip', 'multilinetype'];
 
     for (const input of inputs) {
       expect(
diff --git a/src/test/ts-checker.test.ts b/src/test/ts-checker.test.ts
index 6086f06..ae91844 100644
--- a/src/test/ts-checker.test.ts
+++ b/src/test/ts-checker.test.ts
@@ -207,6 +207,30 @@ describe('ts-checker', () => {
         type: '(elementId: string) => HTMLElement | null',
       },
     ]);
+
+    expect(
+      getAssertions(dedent`
+      const o = {x: 1, y: 2};
+      // type is {
+      //   x: number;
+      //   y: number;
+      // }
+      function addWithExtras(a: number, b: number) {
+        const c = a + b;  // type is number
+        // ...
+        return c;
+      }
+      `),
+    ).toEqual([
+      {
+        line: 0,
+        type: '{ x: number; y: number; }',
+      },
+      {
+        line: 6,
+        type: 'number',
+      },
+    ]);
   });
 
   describe('checkTypeAssertions', () => {