lit-analyzer icon indicating copy to clipboard operation
lit-analyzer copied to clipboard

Problem with custom directives (no-incompatible-type-binding)

Open WickyNilliams opened this issue 4 years ago • 2 comments

I have been trying to write a simple custom directive which simplifies some use-cases for ifDefined:

// instead of
html`<input pattern=${ifDefined(isNumber ? "[0-9]*" : undefined)} />`

// i would like to do this
html`<input pattern=${cond(isNumber, "[0-9]*")} />`

Cribbing from ifDefined, I came up with this directive:

const cond = <T>(condition: boolean, value: T) => (condition ? value : nothing)

This directive works just fine at runtime, but the vscode plugin complains:

Type 'Symbol(undefined) | "[0-9]*"' is not assignable to 'string'

So I also tried creating a directive as recommended in the lit docs:

class ConditionalDirective extends Directive {
  constructor(partInfo: PartInfo) {
    super(partInfo)
    if (partInfo.type !== PartType.ATTRIBUTE) {
      throw new Error("The `cond` directive must be on attributes")
    }
  }

  render(condition: boolean, value: string) {
    return condition ? value : nothing
  }
}

// Create the directive function
cond = directive(ConditionalDirective)

But now i get a different error:

You are binding a non-primitive type 'DirectiveResult<{ prototype: any; new(partInfo: ChildPartInfo | AttributePartInfo | ElementPartInfo) => ConditionalDirective; }>'. 
This could result in binding the string "[object Object]". Use '.' binding instead?

Again, the directive itself works fine at runtime...

I tried following the advice in the error message but then I get a different error again:

Type 'DirectiveResult<{ prototype: any; new(partInfo: ChildPartInfo | AttributePartInfo | ElementPartInfo) => ConditionalDirective; }>' is not assignable to 'string'

Am I doing something wrong here, or is this a bug with lit-analyzer?

WickyNilliams avatar Oct 27 '21 16:10 WickyNilliams

~~Interestingly, the complaints go away if i remove the generics from my basic function directive and make value be any.~~

const cond = (condition: boolean, value: any) => (condition ? value : nothing)

~~Though I don't really want to lose typing. And still the problem persists when extending Directive regardless of how i type the render function~~

Edit: ignore this comment. Of course this works, I'm opting out of type checking with any.

WickyNilliams avatar Oct 27 '21 16:10 WickyNilliams

I'm having the same problem and I actually would expect the attribute binding to work all the same. In my case, I have a directive which does translation <mwc-tab label="${translate(item)}">. The result of that directive is a string. Lit is perfectly content updating the attribute from the directive result.

For now it appears that I have to bind translate(item) as any...

tpluscode avatar Jun 05 '22 09:06 tpluscode