runem / lit-analyzer

Monorepository for tools that analyze lit-html templates
MIT License
318 stars 36 forks source link

Support generic html<T>`...` tagged template literal #75

Open mikelnrd opened 4 years ago

mikelnrd commented 4 years ago

Hi.

I'm using a generic html tagged template literal function in a project, which isn't currently picked up by your plugin.

Is this something you'd consider supporting?

More info...

Having a generic template literal function can be useful in advanced scenarios where you'd like to type-check the values passed to the literal are of a certain type.

Here's a minimal example. My actual usecase involves a complicated custom type, but here I'm just setting the type as string...

function html<V>(
  literals: readonly string[],
  ...values: Array<V>
) {
// implementation
}

// lit-plugin recognises this:
const template = html`<h1 class="example">Hello ${"World"}</h1>`;

// but this isn't recognised but lit-plugin (so no syntax highlighting, code completion, etc)
const template = html<string>`<h1 class="example">Hello ${"World"}</h1>`;

Is this something your plugin could recognise in future?

Thanks for the great plugin by the way!

Mike

PS...

I've tried adding html<string> to "lit-plugin.htmlTemplateTags" in settings but it didn't help (and would be a pain to update for different types in any case).

"lit-plugin.htmlTemplateTags": [
        "html",
        "raw",
        "html<string>"
    ],
herberthobregon commented 4 years ago

In practice it is not feasible to do this. Typescript does not verify the types at runtime.

so you should create an htmlStr or an htmlNumber to validate that it is of one type or another in the implementation. It is not among the cases of use of the plugin

runem commented 4 years ago

@mikelnrd Thanks for your suggestion! I'm happy that you like the plugin :-)

I have split your suggesting into three parts:

1. Syntax Highlighting Regarding syntax highlighting, unfortunately you would have to upstream this issue to https://github.com/mjbvz/vscode-lit-html which is what supports the syntax highlighting-part of this plugin.

2. Type Checking Regarding type checking, I would have to agree with @herberthobregon. Validating these type should be supported directly in Typescript, but it doesn't seem like Typescript fully supports generic tagged template literals as of now (see repro here).

I have however been thinking about lit-specific rules for validating types like undefined/null. See example:

const text: string | undefined;

// this would result in <h1>undefined</h1> which could be catched by a rule in this plugin
html`<h1>${text}</h1>`; 

This may somehow tap into your use case, so I will keep an eye out for your suggestion when I start implementing that feature :+1:

3. Autocompletion I definitely think your use case should support auto completion. This is a low hanging fruit as it can be supported fairly easily :-)

mikelnrd commented 4 years ago

Hi. Thanks @herberthobregon and @runem for taking the time to read the issue and respond.

Regarding (1) Syntax Highlighting, I'll file an issue with mjbvz/vscode-lit-html and link it here. Thanks for the suggestion.

Regarding (3) Autocompletion - that's great news that it's fairly easily for you to add support for auto completion!

Regarding (2) type checking, unless I'm missing something (quite possible!), I do think typescript supports type-checking generic tagged template literals. Check out the examples below (or see the red error underlines in the Typescript playground via this link).

function genericTag1<T>(strings: TemplateStringsArray, ...values: T[]) {
    return "";
}

// string
genericTag1<string>`Hello ${"world"}`; //ok
genericTag1<string>`Hello ${123}`; //type error

declare const world: "world" | undefined;
genericTag1<string>`Hello ${world}`; //type error

// number
genericTag1<number>`Hello ${"world"}`; //type error
genericTag1<number>`Hello ${123}`; //ok

// string | null
genericTag1<string | null>`Hello ${"world"}` //ok
genericTag1<string | null>`Hello ${null}` //ok
genericTag1<string | null>`Hello ${undefined}` //type error

// Custom 'MyType'
interface MyType{
    x: string
    y: number
}
genericTag1<MyType>`Hello ${"world"}` //type error
genericTag1<MyType>`Hello ${{x: "world", y: 123}}` //ok
runem commented 4 years ago

Sorry, my mistake, you're right, - seems like Typescript do support generic template tagged template literals :-)

I'll keep you updated in this issue on my progress on supporting autocompletion :+1: