diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js
index f8b7c64c40e963b448c1aa8cfc189ddc656b7839..d3ab2c4e1e030b51cd78fdbc410d36f2a5455ce4 100644
--- a/packages/react-scripts/config/webpack.config.dev.js
+++ b/packages/react-scripts/config/webpack.config.dev.js
@@ -424,6 +424,7 @@ module.exports = {
         async: false,
         checkSyntacticErrors: true,
         tsconfig: paths.appTsConfig,
+        tslint: require.resolve('tslint-config-react-app'),
         compilerOptions: {
           module: 'esnext',
           moduleResolution: 'node',
diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js
index 5165f1a9842278ef2bd25fa3f034be4efed3ee77..1a72cee05a3ada1bd58bce69a276d11022622340 100644
--- a/packages/react-scripts/config/webpack.config.prod.js
+++ b/packages/react-scripts/config/webpack.config.prod.js
@@ -544,6 +544,7 @@ module.exports = {
         async: false,
         checkSyntacticErrors: true,
         tsconfig: paths.appTsConfig,
+        tslint: require.resolve('tslint-config-react-app'),
         compilerOptions: {
           module: 'esnext',
           moduleResolution: 'node',
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 05d784169f5f4e21d47bc967837e2e946931cf1f..ef3e783bdc32e3b8d93102590e38161573a7f224 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -66,6 +66,9 @@
     "sass-loader": "7.1.0",
     "style-loader": "0.23.0",
     "terser-webpack-plugin": "1.1.0",
+    "tslint": "5.11.0",
+    "tslint-config-react-app": "^1.0.0",
+    "tslint-microsoft-contrib": "5.2.1",
     "url-loader": "1.1.1",
     "webpack": "4.19.1",
     "webpack-dev-server": "3.1.9",
diff --git a/packages/react-scripts/template-typescript/src/index.tsx b/packages/react-scripts/template-typescript/src/index.tsx
index 0c5e75da1cd0651f3c34f0d51aaa6abac634f4f2..d2da559ee477d2fe8719387d7c30d3c9b0da2dc6 100644
--- a/packages/react-scripts/template-typescript/src/index.tsx
+++ b/packages/react-scripts/template-typescript/src/index.tsx
@@ -9,4 +9,5 @@ ReactDOM.render(<App />, document.getElementById('root'));
 // If you want your app to work offline and load faster, you can change
 // unregister() to register() below. Note this comes with some pitfalls.
 // Learn more about service workers: http://bit.ly/CRA-PWA
+// tslint:disable-next-line:no-floating-promises
 serviceWorker.unregister();
diff --git a/packages/react-scripts/template-typescript/src/serviceWorker.ts b/packages/react-scripts/template-typescript/src/serviceWorker.ts
index c0b131051985eb1f95932a77bafcb444df70c07e..5bead095adc64804cbd9ac88f2501cb0865f370d 100644
--- a/packages/react-scripts/template-typescript/src/serviceWorker.ts
+++ b/packages/react-scripts/template-typescript/src/serviceWorker.ts
@@ -39,105 +39,100 @@ export function register(config?: Config) {
       return;
     }
 
-    window.addEventListener('load', () => {
+    window.addEventListener('load', async () => {
       const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
 
       if (isLocalhost) {
         // This is running on localhost. Let's check if a service worker still exists or not.
-        checkValidServiceWorker(swUrl, config);
+        await checkValidServiceWorker(swUrl, config);
 
         // Add some additional logging to localhost, pointing developers to the
         // service worker/PWA documentation.
-        navigator.serviceWorker.ready.then(() => {
-          console.log(
-            'This web app is being served cache-first by a service ' +
-              'worker. To learn more, visit http://bit.ly/CRA-PWA'
-          );
-        });
+        await navigator.serviceWorker.ready;
+
+        console.log(
+          'This web app is being served cache-first by a service ' +
+            'worker. To learn more, visit http://bit.ly/CRA-PWA'
+        );
       } else {
         // Is not localhost. Just register service worker
-        registerValidSW(swUrl, config);
+        await registerValidSW(swUrl, config);
       }
     });
   }
 }
 
-function registerValidSW(swUrl: string, config?: Config) {
-  navigator.serviceWorker
-    .register(swUrl)
-    .then(registration => {
-      registration.onupdatefound = () => {
-        const installingWorker = registration.installing;
-        if (installingWorker == null) {
-          return;
-        }
-        installingWorker.onstatechange = () => {
-          if (installingWorker.state === 'installed') {
-            if (navigator.serviceWorker.controller) {
-              // At this point, the updated precached content has been fetched,
-              // but the previous service worker will still serve the older
-              // content until all client tabs are closed.
-              console.log(
-                'New content is available and will be used when all ' +
-                  'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
-              );
-
-              // Execute callback
-              if (config && config.onUpdate) {
-                config.onUpdate(registration);
-              }
-            } else {
-              // At this point, everything has been precached.
-              // It's the perfect time to display a
-              // "Content is cached for offline use." message.
-              console.log('Content is cached for offline use.');
-
-              // Execute callback
-              if (config && config.onSuccess) {
-                config.onSuccess(registration);
-              }
+async function registerValidSW(swUrl: string, config?: Config) {
+  try {
+    const registration = await navigator.serviceWorker.register(swUrl);
+
+    registration.onupdatefound = () => {
+      const installingWorker = registration.installing;
+      if (!installingWorker) {
+        return;
+      }
+      installingWorker.onstatechange = () => {
+        if (installingWorker.state === 'installed') {
+          if (navigator.serviceWorker.controller) {
+            // At this point, the updated precached content has been fetched,
+            // but the previous service worker will still serve the older
+            // content until all client tabs are closed.
+            console.log(
+              'New content is available and will be used when all ' +
+                'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
+            );
+
+            // Execute callback
+            if (config && config.onUpdate) {
+              config.onUpdate(registration);
+            }
+          } else {
+            // At this point, everything has been precached.
+            // It's the perfect time to display a
+            // "Content is cached for offline use." message.
+            console.log('Content is cached for offline use.');
+
+            // Execute callback
+            if (config && config.onSuccess) {
+              config.onSuccess(registration);
             }
           }
-        };
+        }
       };
-    })
-    .catch(error => {
-      console.error('Error during service worker registration:', error);
-    });
+    };
+  } catch (error) {
+    console.error('Error during service worker registration:', error);
+  }
 }
 
-function checkValidServiceWorker(swUrl: string, config?: Config) {
+async function checkValidServiceWorker(swUrl: string, config?: Config) {
   // Check if the service worker can be found. If it can't reload the page.
-  fetch(swUrl)
-    .then(response => {
-      // Ensure service worker exists, and that we really are getting a JS file.
-      const contentType = response.headers.get('content-type');
-      if (
-        response.status === 404 ||
-        (contentType != null && contentType.indexOf('javascript') === -1)
-      ) {
-        // No service worker found. Probably a different app. Reload the page.
-        navigator.serviceWorker.ready.then(registration => {
-          registration.unregister().then(() => {
-            window.location.reload();
-          });
-        });
-      } else {
-        // Service worker found. Proceed as normal.
-        registerValidSW(swUrl, config);
-      }
-    })
-    .catch(() => {
-      console.log(
-        'No internet connection found. App is running in offline mode.'
-      );
-    });
+  try {
+    const response = await fetch(swUrl);
+
+    // Ensure service worker exists, and that we really are getting a JS file.
+    const contentType = response.headers.get('content-type');
+    if (
+      response.status === 404 ||
+      (contentType && contentType.indexOf('javascript') === -1)
+    ) {
+      // No service worker found. Probably a different app. Reload the page.
+      await unregister();
+      window.location.reload();
+    } else {
+      // Service worker found. Proceed as normal.
+      await registerValidSW(swUrl, config);
+    }
+  } catch {
+    console.log(
+      'No internet connection found. App is running in offline mode.'
+    );
+  }
 }
 
-export function unregister() {
+export async function unregister() {
   if ('serviceWorker' in navigator) {
-    navigator.serviceWorker.ready.then(registration => {
-      registration.unregister();
-    });
+    const registration = await navigator.serviceWorker.ready;
+    await registration.unregister();
   }
 }
diff --git a/packages/react-scripts/template-typescript/tslint.json b/packages/react-scripts/template-typescript/tslint.json
new file mode 100644
index 0000000000000000000000000000000000000000..749319154889c34fe8713a510e5a502666ec3610
--- /dev/null
+++ b/packages/react-scripts/template-typescript/tslint.json
@@ -0,0 +1,3 @@
+{
+    "extends": "tslint-config-react-app"
+}
diff --git a/packages/react-scripts/template/src/serviceWorker.js b/packages/react-scripts/template/src/serviceWorker.js
index 2283ff9ced126ba2ba3cadde952135b3728880f8..e43723e3c5106ec2de8d174c8763dcfdb65e3315 100644
--- a/packages/react-scripts/template/src/serviceWorker.js
+++ b/packages/react-scripts/template/src/serviceWorker.js
@@ -31,105 +31,100 @@ export function register(config) {
       return;
     }
 
-    window.addEventListener('load', () => {
+    window.addEventListener('load', async () => {
       const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
 
       if (isLocalhost) {
         // This is running on localhost. Let's check if a service worker still exists or not.
-        checkValidServiceWorker(swUrl, config);
+        await checkValidServiceWorker(swUrl, config);
 
         // Add some additional logging to localhost, pointing developers to the
         // service worker/PWA documentation.
-        navigator.serviceWorker.ready.then(() => {
-          console.log(
-            'This web app is being served cache-first by a service ' +
-              'worker. To learn more, visit http://bit.ly/CRA-PWA'
-          );
-        });
+        await navigator.serviceWorker.ready;
+
+        console.log(
+          'This web app is being served cache-first by a service ' +
+            'worker. To learn more, visit http://bit.ly/CRA-PWA'
+        );
       } else {
         // Is not localhost. Just register service worker
-        registerValidSW(swUrl, config);
+        await registerValidSW(swUrl, config);
       }
     });
   }
 }
 
-function registerValidSW(swUrl, config) {
-  navigator.serviceWorker
-    .register(swUrl)
-    .then(registration => {
-      registration.onupdatefound = () => {
-        const installingWorker = registration.installing;
-        if (installingWorker == null) {
-          return;
-        }
-        installingWorker.onstatechange = () => {
-          if (installingWorker.state === 'installed') {
-            if (navigator.serviceWorker.controller) {
-              // At this point, the updated precached content has been fetched,
-              // but the previous service worker will still serve the older
-              // content until all client tabs are closed.
-              console.log(
-                'New content is available and will be used when all ' +
-                  'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
-              );
+async function registerValidSW(swUrl, config) {
+  try {
+    const registration = await navigator.serviceWorker.register(swUrl);
+
+    registration.onupdatefound = () => {
+      const installingWorker = registration.installing;
+      if (!installingWorker) {
+        return;
+      }
+      installingWorker.onstatechange = () => {
+        if (installingWorker.state === 'installed') {
+          if (navigator.serviceWorker.controller) {
+            // At this point, the updated precached content has been fetched,
+            // but the previous service worker will still serve the older
+            // content until all client tabs are closed.
+            console.log(
+              'New content is available and will be used when all ' +
+                'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
+            );
 
-              // Execute callback
-              if (config && config.onUpdate) {
-                config.onUpdate(registration);
-              }
-            } else {
-              // At this point, everything has been precached.
-              // It's the perfect time to display a
-              // "Content is cached for offline use." message.
-              console.log('Content is cached for offline use.');
+            // Execute callback
+            if (config && config.onUpdate) {
+              config.onUpdate(registration);
+            }
+          } else {
+            // At this point, everything has been precached.
+            // It's the perfect time to display a
+            // "Content is cached for offline use." message.
+            console.log('Content is cached for offline use.');
 
-              // Execute callback
-              if (config && config.onSuccess) {
-                config.onSuccess(registration);
-              }
+            // Execute callback
+            if (config && config.onSuccess) {
+              config.onSuccess(registration);
             }
           }
-        };
+        }
       };
-    })
-    .catch(error => {
-      console.error('Error during service worker registration:', error);
-    });
+    };
+  } catch (error) {
+    console.error('Error during service worker registration:', error);
+  }
 }
 
-function checkValidServiceWorker(swUrl, config) {
+async function checkValidServiceWorker(swUrl, config) {
   // Check if the service worker can be found. If it can't reload the page.
-  fetch(swUrl)
-    .then(response => {
-      // Ensure service worker exists, and that we really are getting a JS file.
-      const contentType = response.headers.get('content-type');
-      if (
-        response.status === 404 ||
-        (contentType != null && contentType.indexOf('javascript') === -1)
-      ) {
-        // No service worker found. Probably a different app. Reload the page.
-        navigator.serviceWorker.ready.then(registration => {
-          registration.unregister().then(() => {
-            window.location.reload();
-          });
-        });
-      } else {
-        // Service worker found. Proceed as normal.
-        registerValidSW(swUrl, config);
-      }
-    })
-    .catch(() => {
-      console.log(
-        'No internet connection found. App is running in offline mode.'
-      );
-    });
+  try {
+    const response = await fetch(swUrl);
+
+    // Ensure service worker exists, and that we really are getting a JS file.
+    const contentType = response.headers.get('content-type');
+    if (
+      response.status === 404 ||
+      (contentType && contentType.indexOf('javascript') === -1)
+    ) {
+      // No service worker found. Probably a different app. Reload the page.
+      await unregister();
+      window.location.reload();
+    } else {
+      // Service worker found. Proceed as normal.
+      await registerValidSW(swUrl, config);
+    }
+  } catch {
+    console.log(
+      'No internet connection found. App is running in offline mode.'
+    );
+  }
 }
 
-export function unregister() {
+export async function unregister() {
   if ('serviceWorker' in navigator) {
-    navigator.serviceWorker.ready.then(registration => {
-      registration.unregister();
-    });
+    const registration = await navigator.serviceWorker.ready;
+    await registration.unregister();
   }
 }
diff --git a/packages/tslint-config-react-app/index.js b/packages/tslint-config-react-app/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..c12b806ecf63f18b354a1f5518b17ae98df84321
--- /dev/null
+++ b/packages/tslint-config-react-app/index.js
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+module.exports = {
+  defaultSeverity: 'warning',
+  rulesDirectory: 'tslint-microsoft-contrib',
+  rules: {
+    // https://palantir.github.io/tslint/rules/
+    'await-promise': true,
+    'new-parens': true,
+    'no-angle-bracket-type-assertion': true,
+    'no-conditional-assignment': true,
+    'no-debugger': true,
+    'no-duplicate-super': true,
+    'no-duplicate-switch-case': true,
+    'no-duplicate-variable': true,
+    'no-eval': true,
+    'no-floating-promises': true,
+    'no-for-in-array': true,
+    'no-implicit-dependencies': [true, 'dev'],
+    'no-invalid-template-strings': true,
+    'no-invalid-this': true,
+    'no-namespace': true,
+    'no-sparse-arrays': true,
+    'no-string-throw': true,
+    'no-switch-case-fall-through': true,
+    'no-unused-expression': [true, 'allow-fast-null-checks'],
+    // DEPRECATED. Recommended to use TS 'noUnusedLocals' for now
+    // "no-unused-variable": true,
+    'triple-equals': true,
+    'use-isnan': true,
+
+    // https://github.com/Microsoft/tslint-microsoft-contrib
+    'react-a11y-anchors': true,
+    'react-a11y-aria-unsupported-elements': true,
+    'react-a11y-event-has-role': true,
+    'react-a11y-image-button-has-alt': true,
+    'react-a11y-img-has-alt': true,
+    'react-a11y-props': true,
+    'react-a11y-proptypes': true,
+    'react-a11y-role': true,
+    'react-a11y-role-has-required-aria-props': true,
+    'react-a11y-role-supports-aria-props': true,
+  },
+};
diff --git a/packages/tslint-config-react-app/package.json b/packages/tslint-config-react-app/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..4fb5b10aa7f9e91bce9c225f3803d6ad59ebad7d
--- /dev/null
+++ b/packages/tslint-config-react-app/package.json
@@ -0,0 +1,15 @@
+{
+  "name": "tslint-config-react-app",
+  "version": "1.0.0",
+  "description": "TSLint configuration used by Create React App",
+  "repository": "facebook/create-react-app",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/facebook/create-react-app/issues"
+  },
+  "main": "index.js",
+  "peerDependencies": {
+    "tslint": "5.x",
+    "tslint-microsoft-contrib": "5.x"
+  }
+}