Repository: sandersn/downlevel-dts
Pull request #61 (2022-01-04 01:04:02)

Downlevel IteratorResult for TS < v3.6
Fixes https://github.com/sandersn/downlevel-dts/issues/40

[`IteratorResult` definition changed in TS v3.6 to have another type argument for return expressions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html#stricter-generators). This PR adds downleveling for this type by removing that second type variable for TS v < 3.6.

sandersn: One initial comment: 3.5 is 3 years old and outside the Definitely Typed/Visual Studio 2-year support window, so you might be able to drop 3.5 support as well at some point.

I'm tempted to adopt a support window for downlevel-dts someday, but since its purpose is backward compatibility, I don't know if that makes sense. From the beginning it's never supported `typescript@2`, for example.
deyaaeldeen: > Sorry, I wasn't clear enough. I like isTypeReference, but I meant the 3-line check for whether a symbol is from the typescript lib folder inside node_modules.

My bad, I did not address all your feedback at once. I think I did now though, how does it look?

Patch:
diff --git a/index.js b/index.js
index a9456eb..5b2a506 100644
--- a/index.js
+++ b/index.js
@@ -118,6 +118,12 @@ function doTransform(checker, targetVersion, k) {
           )
         );
       }
+    } else if (semver.lt(targetVersion, "3.6.0") && isTypeReference(n, "IteratorResult")) {
+      const symbol = checker.getSymbolAtLocation(ts.isTypeReferenceNode(n) ? n.typeName : n.expression);
+      const typeArguments = n.typeArguments;
+      if (isStdLibSymbol(symbol) && typeArguments) {
+        return ts.createTypeReferenceNode(ts.createIdentifier("IteratorResult"), [typeArguments[0]]);
+      }
     } else if (
       semver.lt(targetVersion, "3.8.0") &&
       ts.isPropertyDeclaration(n) &&
@@ -167,20 +173,11 @@ function doTransform(checker, targetVersion, k) {
       return ts.createExportDeclaration(n.decorators, n.modifiers, n.exportClause, n.moduleSpecifier);
     } else if (semver.lt(targetVersion, "3.8.0") && ts.isImportClause(n) && n.isTypeOnly) {
       return ts.createImportClause(n.name, n.namedBindings);
-    } else if (
-      (ts.isTypeReferenceNode(n) && ts.isIdentifier(n.typeName) && n.typeName.escapedText === "Omit") ||
-      (ts.isExpressionWithTypeArguments(n) && ts.isIdentifier(n.expression) && n.expression.escapedText === "Omit")
-    ) {
+    } else if (isTypeReference(n, "Omit")) {
       const symbol = checker.getSymbolAtLocation(ts.isTypeReferenceNode(n) ? n.typeName : n.expression);
       const typeArguments = n.typeArguments;
 
-      if (
-        semver.lt(targetVersion, "3.5.0") &&
-        symbol &&
-        symbol.declarations.length &&
-        symbol.declarations[0].getSourceFile().fileName.includes("node_modules/typescript/lib/lib") &&
-        typeArguments
-      ) {
+      if (semver.lt(targetVersion, "3.5.0") && isStdLibSymbol(symbol) && typeArguments) {
         return ts.createTypeReferenceNode(ts.createIdentifier("Pick"), [
           typeArguments[0],
           ts.createTypeReferenceNode(ts.createIdentifier("Exclude"), [
@@ -278,3 +275,31 @@ function flatMap(l, f) {
   }
   return acc;
 }
+
+/**
+ * Checks whether a node is a type reference with typeName as a name
+ * @param {ts.Node} node AST node
+ * @param {string} typeName name of the type
+ * @returns true if the node is a type reference with typeName as a name
+ */
+function isTypeReference(node, typeName) {
+  return (
+    (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.escapedText === typeName) ||
+    (ts.isExpressionWithTypeArguments(node) &&
+      ts.isIdentifier(node.expression) &&
+      node.expression.escapedText === typeName)
+  );
+}
+
+/**
+ * Returns whether a symbol is a standard TypeScript library definition
+ * @param {ts.Symbol | undefined} symbol a symbol in source file
+ * @returns whether this symbol is for a standard TypeScript library definition
+ */
+function isStdLibSymbol(symbol) {
+  return (
+    symbol &&
+    symbol.declarations.length &&
+    symbol.declarations[0].getSourceFile().fileName.includes("node_modules/typescript/lib/lib")
+  );
+}
diff --git a/package-lock.json b/package-lock.json
index d2df97a..1f88233 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "downlevel-dts",
-  "version": "0.7.0",
+  "version": "0.8.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -882,6 +882,16 @@
         "tweetnacl": "^0.14.3"
       }
     },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1634,6 +1644,13 @@
         "bser": "2.1.1"
       }
     },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "dev": true,
+      "optional": true
+    },
     "fill-range": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -1703,6 +1720,17 @@
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
       "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
+    "fsevents": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+      "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "bindings": "^1.5.0",
+        "nan": "^2.12.1"
+      }
+    },
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -2512,6 +2540,7 @@
         "@jest/types": "^24.9.0",
         "anymatch": "^2.0.0",
         "fb-watchman": "^2.0.0",
+        "fsevents": "^1.2.7",
         "graceful-fs": "^4.1.15",
         "invariant": "^2.2.4",
         "jest-serializer": "^24.9.0",
@@ -3169,6 +3198,13 @@
         "minimatch": "^3.0.4"
       }
     },
+    "nan": {
+      "version": "2.15.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
+      "dev": true,
+      "optional": true
+    },
     "nanomatch": {
       "version": "1.2.13",
       "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
diff --git a/package.json b/package.json
index fd89047..d6fc902 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "downlevel-dts",
-  "version": "0.7.0",
+  "version": "0.8.0",
   "description": "Convert d.ts to be compatible with older typescript compilers",
   "homepage": "https://github.com/sandersn/downlevel-dts",
   "main": "index.js",


Test patch:
diff --git a/baselines/reference/ts3.4/test.d.ts b/baselines/reference/ts3.4/test.d.ts
index 127e736..b250db7 100644
--- a/baselines/reference/ts3.4/test.d.ts
+++ b/baselines/reference/ts3.4/test.d.ts
@@ -56,3 +56,4 @@ export declare const foo: {
         baz: <T>(val: unknown) => void;
     };
 };
+export type IR = IteratorResult<number>;
diff --git a/baselines/reference/ts3.5/test.d.ts b/baselines/reference/ts3.5/test.d.ts
index bbdda3d..77f7998 100644
--- a/baselines/reference/ts3.5/test.d.ts
+++ b/baselines/reference/ts3.5/test.d.ts
@@ -56,3 +56,4 @@ export declare const foo: {
         baz: <T>(val: unknown) => void;
     };
 };
+export type IR = IteratorResult<number>;
diff --git a/baselines/reference/ts3.6/test.d.ts b/baselines/reference/ts3.6/test.d.ts
index 34d78b0..5c68c90 100644
--- a/baselines/reference/ts3.6/test.d.ts
+++ b/baselines/reference/ts3.6/test.d.ts
@@ -58,3 +58,4 @@ export declare const foo: {
         baz: <T>(val: unknown) => void;
     };
 };
+export type IR = IteratorResult<number, string>;
diff --git a/baselines/reference/ts3.7/test.d.ts b/baselines/reference/ts3.7/test.d.ts
index 2644188..7057773 100644
--- a/baselines/reference/ts3.7/test.d.ts
+++ b/baselines/reference/ts3.7/test.d.ts
@@ -58,3 +58,4 @@ export declare const foo: {
         baz: <T>(val: unknown) => asserts val is T;
     };
 };
+export type IR = IteratorResult<number, string>;
diff --git a/baselines/reference/ts3.8/test.d.ts b/baselines/reference/ts3.8/test.d.ts
index 6f13279..e2ba90d 100644
--- a/baselines/reference/ts3.8/test.d.ts
+++ b/baselines/reference/ts3.8/test.d.ts
@@ -56,3 +56,4 @@ export declare const foo: {
         baz: <T>(val: unknown) => asserts val is T;
     };
 };
+export type IR = IteratorResult<number, string>;
diff --git a/baselines/reference/ts3.9/test.d.ts b/baselines/reference/ts3.9/test.d.ts
index 6f13279..e2ba90d 100644
--- a/baselines/reference/ts3.9/test.d.ts
+++ b/baselines/reference/ts3.9/test.d.ts
@@ -56,3 +56,4 @@ export declare const foo: {
         baz: <T>(val: unknown) => asserts val is T;
     };
 };
+export type IR = IteratorResult<number, string>;
diff --git a/baselines/reference/ts4.0/test.d.ts b/baselines/reference/ts4.0/test.d.ts
index 1c8058d..44cdd0d 100644
--- a/baselines/reference/ts4.0/test.d.ts
+++ b/baselines/reference/ts4.0/test.d.ts
@@ -56,3 +56,4 @@ export declare const foo: {
         baz: <T>(val: unknown) => asserts val is T;
     };
 };
+export type IR = IteratorResult<number, string>;
diff --git a/test/test.d.ts b/test/test.d.ts
index 5e2c9e9..29acbf2 100644
--- a/test/test.d.ts
+++ b/test/test.d.ts
@@ -61,3 +61,5 @@ export declare const foo: {
         baz: <T>(val: unknown) => asserts val is T;
     };
 };
+
+export type IR = IteratorResult<number, string>;
\ No newline at end of file