From d860f5cabeabe2cdeb12ea62be2e58c27815841b Mon Sep 17 00:00:00 2001
From: 43081j <43081j@users.noreply.github.com>
Date: Sun, 10 May 2020 15:22:01 +0100
Subject: [PATCH] add event bindings to incompatible type binding rule
---
.../src/rules/no-incompatible-type-binding.ts | 2 ++
.../type/is-assignable-in-event-binding.ts | 36 +++++++++++++++++++
.../test/helpers/generate-test-file.ts | 3 +-
.../rules/no-incompatible-type-binding.ts | 20 +++++++++++
4 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 packages/lit-analyzer/src/rules/util/type/is-assignable-in-event-binding.ts
diff --git a/packages/lit-analyzer/src/rules/no-incompatible-type-binding.ts b/packages/lit-analyzer/src/rules/no-incompatible-type-binding.ts
index ebaff2ec..187ebf9e 100644
--- a/packages/lit-analyzer/src/rules/no-incompatible-type-binding.ts
+++ b/packages/lit-analyzer/src/rules/no-incompatible-type-binding.ts
@@ -8,6 +8,7 @@ import { extractBindingTypes } from "./util/type/extract-binding-types";
import { isAssignableInAttributeBinding } from "./util/type/is-assignable-in-attribute-binding";
import { isAssignableInBooleanBinding } from "./util/type/is-assignable-in-boolean-binding";
import { isAssignableInPropertyBinding } from "./util/type/is-assignable-in-property-binding";
+import { isAssignableInEventBinding } from "./util/type/is-assignable-in-event-binding";
/**
* This rule validate if the types of a binding are assignable.
@@ -33,6 +34,7 @@ const rule: RuleModule = {
break;
case LIT_HTML_EVENT_LISTENER_ATTRIBUTE_MODIFIER:
+ isAssignableInEventBinding(htmlAttr, { typeA, typeB }, context);
break;
default: {
diff --git a/packages/lit-analyzer/src/rules/util/type/is-assignable-in-event-binding.ts b/packages/lit-analyzer/src/rules/util/type/is-assignable-in-event-binding.ts
new file mode 100644
index 00000000..7c16906d
--- /dev/null
+++ b/packages/lit-analyzer/src/rules/util/type/is-assignable-in-event-binding.ts
@@ -0,0 +1,36 @@
+import { SimpleType, SimpleTypeKind, toTypeString } from "ts-simple-type";
+import { HtmlNodeAttr } from "../../../analyze/types/html-node/html-node-attr-types";
+import { RuleModuleContext } from "../../../analyze/types/rule/rule-module-context";
+import { rangeFromHtmlNodeAttr } from "../../../analyze/util/range-util";
+import { isAssignableToType } from "./is-assignable-to-type";
+
+export function isAssignableInEventBinding(
+ htmlAttr: HtmlNodeAttr,
+ { typeA, typeB }: { typeA: SimpleType; typeB: SimpleType },
+ context: RuleModuleContext
+): boolean | undefined {
+ const expectedType: SimpleType = {
+ kind: SimpleTypeKind.FUNCTION,
+ returnType: { kind: SimpleTypeKind.VOID },
+ argTypes: [
+ {
+ name: 'event',
+ type: typeA,
+ optional: false,
+ spread: false,
+ initializer: false
+ }
+ ]
+ };
+
+ if (!isAssignableToType({ typeA: expectedType, typeB }, context)) {
+ context.report({
+ location: rangeFromHtmlNodeAttr(htmlAttr),
+ message: `Type '${toTypeString(typeB)}' is not assignable to '${toTypeString(expectedType)}'`
+ });
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/packages/lit-analyzer/test/helpers/generate-test-file.ts b/packages/lit-analyzer/test/helpers/generate-test-file.ts
index 210f9d6b..2fff92b4 100644
--- a/packages/lit-analyzer/test/helpers/generate-test-file.ts
+++ b/packages/lit-analyzer/test/helpers/generate-test-file.ts
@@ -1,11 +1,12 @@
import { TestFile } from "./compile-files";
-export function makeElement({ properties, slots }: { properties?: string[]; slots?: string[] }): TestFile {
+export function makeElement({ properties, slots, events }: { properties?: string[]; slots?: string[]; events?: string[] }): TestFile {
return {
fileName: "my-element.ts",
text: `
/**
${(slots || []).map(slot => ` * @slot ${slot}`)}
+${(events || []).map(event => ` * @fires ${event}`)}
*/
class MyElement extends HTMElement {
${(properties || []).map(prop => `@property() ${prop}`).join("\n")}
diff --git a/packages/lit-analyzer/test/rules/no-incompatible-type-binding.ts b/packages/lit-analyzer/test/rules/no-incompatible-type-binding.ts
index 50c47b90..5412f45a 100644
--- a/packages/lit-analyzer/test/rules/no-incompatible-type-binding.ts
+++ b/packages/lit-analyzer/test/rules/no-incompatible-type-binding.ts
@@ -175,3 +175,23 @@ test("Attribute binding: 'guard' directive correctly infers correct type from th
const { diagnostics } = getDiagnostics('type guard = Function; html` ({} as string | number))}" />`');
hasDiagnostic(t, diagnostics, "no-incompatible-type-binding");
});
+
+test("Event binding: event handler is assignable to valid event", t => {
+ const { diagnostics } = getDiagnostics([makeElement({ events: ["foo-event"] }), 'html` {}}>`']);
+ hasNoDiagnostics(t, diagnostics);
+});
+
+test("Event binding: event handler is assignable to valid typed event", t => {
+ const { diagnostics } = getDiagnostics([makeElement({ events: ["{MouseEvent} foo-event"] }), 'html` {}}>`']);
+ hasNoDiagnostics(t, diagnostics);
+});
+
+test("Event binding: invalid event handler is not assignable to typed event", t => {
+ const { diagnostics } = getDiagnostics([makeElement({ events: ["{MouseEvent} foo-event"] }), 'html` {}}>`']);
+ hasDiagnostic(t, diagnostics, "no-incompatible-type-binding");
+});
+
+test("Event binding: invalid event handler is not assignable to event", t => {
+ const { diagnostics } = getDiagnostics([makeElement({ events: ["foo-event"] }), 'html` {}}>`']);
+ hasDiagnostic(t, diagnostics, "no-incompatible-type-binding");
+});