Refactor options page to use Typescript

Also, I've added and ran eslint, and fixed several minor issues accross
the Typescript codebase.

Bug: translateselectedtext:15
Change-Id: I8cfd67697f9bfb22f6de93b64fd750de66bab863
diff --git a/src/options/elements/options-editor/add-language-dialog.js b/src/options/elements/options-editor/add-language-dialog.ts
similarity index 72%
rename from src/options/elements/options-editor/add-language-dialog.js
rename to src/options/elements/options-editor/add-language-dialog.ts
index 4cfd711..155d4e5 100644
--- a/src/options/elements/options-editor/add-language-dialog.js
+++ b/src/options/elements/options-editor/add-language-dialog.ts
@@ -1,23 +1,30 @@
 import {css, html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
 
-import {isoLangs} from '../../../common/consts';
+import {IsoLang, isoLangs} from '../../../common/consts';
 import {msg} from '../../../common/i18n';
+import {TargetLangs} from '../../../common/options';
 import {DIALOG_STYLES} from '../../shared/dialog-styles';
 import {SHARED_STYLES} from '../../shared/shared-styles';
 
+interface IsoLangWCode extends IsoLang {
+  code: string;
+}
+
 const ALL_LANGUAGES =
     Object.entries(isoLangs)
         .map(entry => {
-          let lang = entry[1];
-          lang.code = entry[0];
+          const lang: IsoLangWCode = Object.assign(entry[1], {code: entry[0]});
           return lang;
         })
         .sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0));
 
-export class AddLanguageDialog extends LitElement {
+@customElement('add-language-dialog')
+export default class AddLanguageDialog extends LitElement {
   static properties = {
     languages: {type: Object},
   };
+  @property({type: Object}) languages: TargetLangs;
 
   static get styles() {
     return [
@@ -51,8 +58,8 @@
   }
 
   render() {
-    let languageCodes = Object.values(this.languages ?? {});
-    let languages = ALL_LANGUAGES
+    const languageCodes = Object.values(this.languages ?? {});
+    const languages = ALL_LANGUAGES
                         .filter(lang => {
                           return !languageCodes.includes(lang.code);
                         })
@@ -88,7 +95,7 @@
   }
 
   showDialog() {
-    let dialog = this.renderRoot.querySelector('dialog');
+    const dialog = this.renderRoot.querySelector('dialog');
     dialog.showModal();
   }
 
@@ -97,16 +104,15 @@
   }
 
   addLanguage() {
-    let languageCodes = Object.values(this.languages ?? {});
-    let select = this.renderRoot.querySelector('#select_language');
+    const languageCodes = Object.values(this.languages ?? {});
+    const select = this.renderRoot.querySelector('#select_language') as HTMLSelectElement;
 
-    let newLang = select.value;
+    const newLang = select.value;
     languageCodes.push(newLang);
-    let translateinto = Object.assign({}, languageCodes);
+    const translateinto = Object.assign({}, languageCodes);
     chrome.storage.sync.set({translateinto}, () => {
       select.selectedIndex = 0;
       this.closeDialog();
     });
   }
 }
-customElements.define('add-language-dialog', AddLanguageDialog);
diff --git a/src/options/elements/options-editor/languages-editor.js b/src/options/elements/options-editor/languages-editor.ts
similarity index 83%
rename from src/options/elements/options-editor/languages-editor.js
rename to src/options/elements/options-editor/languages-editor.ts
index 76d9967..64c3f84 100644
--- a/src/options/elements/options-editor/languages-editor.js
+++ b/src/options/elements/options-editor/languages-editor.ts
@@ -1,16 +1,17 @@
+import './add-language-dialog';
+
 import {css, html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
 import {map} from 'lit/directives/map.js';
 
 import {isoLangs} from '../../../common/consts';
 import {msg} from '../../../common/i18n';
+import {TargetLangs} from '../../../common/options';
 import {SHARED_STYLES} from '../../shared/shared-styles';
 
-import AddLanguageDialog from './add-language-dialog';
-
-export class LanguagesEditor extends LitElement {
-  static properties = {
-    languages: {type: Object},
-  };
+@customElement('languages-editor')
+export default class LanguagesEditor extends LitElement {
+  @property({type: Object}) languages: TargetLangs;
 
   static get styles() {
     return [
@@ -84,16 +85,10 @@
     ];
   }
 
-  constructor() {
-    super();
-    this.addEventListener('show-credits-dialog', this.showDialog);
-    this.sortable = undefined;
-  }
-
   render() {
-    let languageCodes = Object.values(this.languages ?? {});
-    let languageList = map(languageCodes, (lang, i) => {
-      let moveBtns = [];
+    const languageCodes = Object.values(this.languages ?? {});
+    const languageList = map(languageCodes, (lang, i) => {
+      const moveBtns = [];
       if (i != 0) {
         moveBtns.push(html`
           <button
@@ -160,29 +155,28 @@
     this.renderRoot.querySelector('add-language-dialog').dispatchEvent(e);
   }
 
-  save(languageCodes) {
-    let translateinto = Object.assign({}, languageCodes);
+  save(languageCodes: string[]) {
+    const translateinto = Object.assign({}, languageCodes);
     chrome.storage.sync.set({translateinto});
   }
 
-  deleteLanguage(deleteLang) {
-    let languageCodes =
+  deleteLanguage(deleteLang: string) {
+    const languageCodes =
         Object.values(this.languages ?? {}).filter(lang => lang != deleteLang);
     this.save(languageCodes);
   }
 
-  swapLanguages(i, j) {
-    let languageCodes = Object.values(this.languages ?? {});
+  swapLanguages(i: number, j: number) {
+    const languageCodes = Object.values(this.languages ?? {});
     if (i >= languageCodes.length || j >= languageCodes.length || i < 0 ||
         j < 0) {
       console.error(
           'Can\'t swap languages because the indexes are out of the range.');
       return;
     }
-    let tmp = languageCodes[j];
+    const tmp = languageCodes[j];
     languageCodes[j] = languageCodes[i];
     languageCodes[i] = tmp;
     this.save(languageCodes);
   }
 }
-customElements.define('languages-editor', LanguagesEditor);
diff --git a/src/options/elements/options-editor/options-editor.js b/src/options/elements/options-editor/options-editor.ts
similarity index 67%
rename from src/options/elements/options-editor/options-editor.js
rename to src/options/elements/options-editor/options-editor.ts
index a26072e..2d0422e 100644
--- a/src/options/elements/options-editor/options-editor.js
+++ b/src/options/elements/options-editor/options-editor.ts
@@ -1,16 +1,16 @@
+import './languages-editor';
+
 import {css, html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
 import {map} from 'lit/directives/map.js';
 
 import {msg} from '../../../common/i18n';
-import {TAB_OPTIONS} from '../../../common/options';
+import {OptionsV0, TAB_OPTIONS, TabOptionValue} from '../../../common/options';
 import {SHARED_STYLES} from '../../shared/shared-styles';
 
-import LanguagesEditor from './languages-editor';
-
-export class OptionsEditor extends LitElement {
-  static properties = {
-    storageData: {type: Object},
-  };
+@customElement('options-editor')
+export default class OptionsEditor extends LitElement {
+  @property({type: Object}) storageData: OptionsV0;
 
   static get styles() {
     return [
@@ -24,16 +24,11 @@
     ];
   }
 
-  constructor() {
-    super();
-    this.addEventListener('show-credits-dialog', this.showDialog);
-  }
-
   render() {
-    let currentTabOption = this.storageData?.uniquetab;
+    const currentTabOption = this.storageData?.uniquetab;
 
-    let otherOptions = map(TAB_OPTIONS, (option, i) => {
-      let checked = option.value == currentTabOption ||
+    const otherOptions = map(TAB_OPTIONS, (option, i) => {
+      const checked = option.value == currentTabOption ||
           option.deprecatedValues.includes(currentTabOption);
       return html`
             <p>
@@ -56,10 +51,9 @@
     `;
   }
 
-  changeTabOption(value) {
+  changeTabOption(value: TabOptionValue) {
     chrome.storage.sync.set({uniquetab: value}, function() {
       chrome.runtime.sendMessage({action: 'clearTranslatorTab'});
     });
   }
 }
-customElements.define('options-editor', OptionsEditor);