diff --git a/components/filter/test/golden/filter/chromium/single-set-searched-search-then-clear.png b/components/filter/test/golden/filter/chromium/single-set-searched-search-then-clear.png
index a76f7c223a6..49d7067c3f4 100644
Binary files a/components/filter/test/golden/filter/chromium/single-set-searched-search-then-clear.png and b/components/filter/test/golden/filter/chromium/single-set-searched-search-then-clear.png differ
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 00000000000..5de68b6328b
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "module": "ES2020",
+ "target": "ES2020",
+ "typeRoots": ["./typings", "./node_modules/@types"],
+ "checkJs": false,
+ "moduleResolution": "bundler"
+ },
+ "include": ["**/*", "**/*.js"],
+ "exclude": ["node_modules", "./tools/**/*"]
+}
diff --git a/package.json b/package.json
index 32489b6ce87..75f2899f817 100644
--- a/package.json
+++ b/package.json
@@ -13,11 +13,15 @@
"build:illustrations": "node ./cli/empty-state-illustration-generator.js",
"build:sass": "sass ./test/sass.scss > ./test/sass.output.css",
"build:wca": "wca analyze \"{components,templates}/**/*.js\" --format json --outFile custom-elements.json",
- "build": "npm run build:clean && npm run build:icons && npm run build:illustrations && npm run build:sass && npm run build:wca",
+ "build:lit-mappings": "sh ./tools/create-mapping.sh",
+ "build": "npm run build:clean && npm run build:icons && npm run build:illustrations && npm run build:sass && npm run build:wca && npm run build:lit-mappings",
"build-static": "rollup -c ./rollup/rollup.config.js",
- "lint": "npm run lint:eslint && npm run lint:style",
+ "lint": "npm run lint:eslint && npm run lint:style && npm run lint:types",
"lint:eslint": "eslint .",
"lint:style": "stylelint \"**/*.{js,html}\" --ignore-path .gitignore",
+ "prelint:types": "cp typings/mapping.d.ts typings/mapping.ts",
+ "lint:types": "tsc -p tools/check-html-element-tag-types/tsconfig.json",
+ "postlint:types": "rm typings/mapping.ts",
"start": "web-dev-server --node-resolve --watch --open",
"test": "npm run lint && npm run test:translations && npm run test:unit && npm run test:axe",
"test:axe": "d2l-test-runner axe --chrome",
@@ -37,11 +41,13 @@
"/mixins",
"/templates",
"/tools",
+ "/typings/mapping.d.ts",
"!demo",
"!test",
"/components/demo",
"!/components/demo/demo"
],
+ "types": "./typings/mapping.d.ts",
"author": "D2L Corporation",
"license": "Apache-2.0",
"devDependencies": {
diff --git a/tools/check-html-element-tag-types/check-html-element-helper.d.ts b/tools/check-html-element-tag-types/check-html-element-helper.d.ts
new file mode 100644
index 00000000000..afdd798c3ec
--- /dev/null
+++ b/tools/check-html-element-tag-types/check-html-element-helper.d.ts
@@ -0,0 +1,13 @@
+/**
+ * Set Element constructors to something generic so that check-html-element-tag-types
+ * will fail if one of the classes in the mixin chain doesn't extend HTMLElement.
+ * Otherwise, the mixins will default to the element mixin
+ */
+import { Constructor } from '@open-wc/dedupe-mixin';
+
+declare global {
+ type LitElementConstructor = Constructor<{}>;
+ type LitElementClassType = Constructor<{}>;
+ type ReactiveElementConstructor = Constructor<{}>;
+ type ReactiveElementClassType = Constructor<{}>;
+}
diff --git a/tools/check-html-element-tag-types/check-html-element-tag-types.ts b/tools/check-html-element-tag-types/check-html-element-tag-types.ts
new file mode 100644
index 00000000000..523c9223c9c
--- /dev/null
+++ b/tools/check-html-element-tag-types/check-html-element-tag-types.ts
@@ -0,0 +1,6 @@
+///
+///
+
+type Check = K;
+// This line fails if the mapping contains something that doesn't extend HTMLElement
+type Test = Check;
diff --git a/tools/check-html-element-tag-types/tsconfig.json b/tools/check-html-element-tag-types/tsconfig.json
new file mode 100644
index 00000000000..366567814af
--- /dev/null
+++ b/tools/check-html-element-tag-types/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "compilerOptions": {
+ "module": "ES2020",
+ "target": "ES2020",
+ "allowJs": true,
+ "checkJs": false,
+ "noEmit": true,
+ "strict": true,
+ "strictFunctionTypes": false,
+ "moduleResolution": "bundler",
+ "maxNodeModuleJsDepth": 3,
+ "skipLibCheck": true
+ },
+ "files": ["check-html-element-tag-types.ts"]
+ }
+
\ No newline at end of file
diff --git a/tools/create-mapping.sh b/tools/create-mapping.sh
new file mode 100644
index 00000000000..6aea704ca45
--- /dev/null
+++ b/tools/create-mapping.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+FILES=`egrep --include="*.js" --exclude-dir="node_modules" --exclude-dir="test" -lR "customElements.define" * | xargs egrep -L 'export class.*Test.* extends'`
+
+FILES_WITH_TYPEDEF=`egrep -l "@typedef.*Exported" $FILES`
+FILES_WITH_EXPORT=`egrep -l "export class.*" $FILES`
+
+echo "/** Generated from {@link ../tools/create-mapping.sh} */" > ./typings/mapping.d.ts
+echo "import { LitElement } from 'lit';" >> ./typings/mapping.d.ts
+echo "declare global {" >> ./typings/mapping.d.ts
+echo " interface HTMLElementTagNameMap {" >> ./typings/mapping.d.ts
+if [ "x$FILES_WITH_TYPEDEF" != "x" ]
+then
+ echo " // Components with @typedef exports" >> ./typings/mapping.d.ts
+ egrep -oR "customElements.define\(['\"].*\)" $FILES_WITH_TYPEDEF | sed -r "s/(.*):customElements\.define\((.*),\s*(.*)\)/ \2: import('..\/\1').\3Exported;/" >> ./typings/mapping.d.ts
+fi
+if [ "x$FILES_WITH_EXPORT" != "x" ]
+then
+ echo " // Components with export class...extends LitElement" >> ./typings/mapping.d.ts
+ egrep -oR "customElements.define\(['\"].*\)" $FILES_WITH_EXPORT | sed -r "s/(.*):customElements\.define\((.*),\s*(.*)\)/ \2: import('..\/\1').\3;/" >> ./typings/mapping.d.ts
+fi
+echo " }" >> ./typings/mapping.d.ts
+echo "}" >> ./typings/mapping.d.ts
diff --git a/typings/common.d.ts b/typings/common.d.ts
new file mode 100644
index 00000000000..c09df036155
--- /dev/null
+++ b/typings/common.d.ts
@@ -0,0 +1,14 @@
+import { Constructor } from '@open-wc/dedupe-mixin';
+import { LitElement, ReactiveElement } from 'lit';
+
+declare global {
+ // Used for mixins that use lit-html but don't set properties or styles
+ type LitElementConstructor = Constructor;
+ // Used for mixins that use lit-html and set properties or styles
+ type LitElementClassType = LitElementConstructor & Pick;
+
+ // Used for mixins that use reactive element properties (updated, willUpdate, firstUpdated, connectedCallback, etc) but don't set properties or styles
+ type ReactiveElementConstructor = Constructor;
+ // Used for mixins that use reactive element properties (updated, willUpdate, firstUpdated, connectedCallback, etc) and set properties or styles
+ type ReactiveElementClassType = ReactiveElementConstructor & Pick;
+}
diff --git a/typings/mapping.d.ts b/typings/mapping.d.ts
new file mode 100644
index 00000000000..b011c509c61
--- /dev/null
+++ b/typings/mapping.d.ts
@@ -0,0 +1,8 @@
+/** Generated from {@link ../tools/create-mapping.sh} */
+import { LitElement } from 'lit';
+declare global {
+ interface HTMLElementTagNameMap {
+ // Components with export class...extends LitElement
+ 'd2l-build-info': import('../index.js').BuildInfo;
+ }
+}