Skip to content

Commit

Permalink
add event bindings to incompatible type binding rule
Browse files Browse the repository at this point in the history
  • Loading branch information
43081j committed May 10, 2020
1 parent 950d0e3 commit d860f5c
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -33,6 +34,7 @@ const rule: RuleModule = {
break;

case LIT_HTML_EVENT_LISTENER_ATTRIBUTE_MODIFIER:
isAssignableInEventBinding(htmlAttr, { typeA, typeB }, context);
break;

default: {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 2 additions & 1 deletion packages/lit-analyzer/test/helpers/generate-test-file.ts
Original file line number Diff line number Diff line change
@@ -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")}
Expand Down
20 changes: 20 additions & 0 deletions packages/lit-analyzer/test/rules/no-incompatible-type-binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,23 @@ test("Attribute binding: 'guard' directive correctly infers correct type from th
const { diagnostics } = getDiagnostics('type guard = Function; html`<input maxlength="${guard([""], () => ({} 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`<my-element @foo-event=${(ev) => {}}></my-element>`']);
hasNoDiagnostics(t, diagnostics);
});

test("Event binding: event handler is assignable to valid typed event", t => {
const { diagnostics } = getDiagnostics([makeElement({ events: ["{MouseEvent} foo-event"] }), 'html`<my-element @foo-event=${(ev: MouseEvent) => {}}></my-element>`']);
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`<my-element @foo-event=${(ev: KeyboardEvent) => {}}></my-element>`']);
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`<my-element @foo-event=${(arg: boolean) => {}}></my-element>`']);
hasDiagnostic(t, diagnostics, "no-incompatible-type-binding");
});

0 comments on commit d860f5c

Please sign in to comment.