hybridsjs / hybrids

Extraordinary JavaScript UI framework with unique declarative and functional architecture
https://hybrids.js.org
MIT License
3.03k stars 85 forks source link

using 'host.render()' and 'host.content()' causes a TypeScript error #239

Closed Qsppl closed 5 months ago

Qsppl commented 5 months ago
import { Model, define, html, store } from "https://esm.sh/hybrids@8.2.11"

export interface IModel {
    prop1: number
}

export const ModelStore: Model<IModel> = {
    id: true,
    prop1: 1
}

function toggleEdit(host: ISecondComponent) {
    host.editMode = true
    host.draft = host.model
    host.content().querySelector('input').focus()
}

export interface ISecondComponent extends HTMLElement {
    model: IModel
    draft: IModel
    editMode: boolean
}

export default define<ISecondComponent>({
    tag: "a-second",
    model: store(ModelStore),
    draft: store(ModelStore, { draft: true }),
    editMode: {
        get: (host, lastValue) => !host.model || lastValue,
        set: (host, value, lastValue) => value,
    },
    content: ({ draft, model, editMode }) => html`
        ${editMode ? html`
            <form>
                <input value="${draft.prop1}" oninput="${html.set(draft, 'prop1')}" />
                <button type="submit">Save</button>
            </form>
        ` : html`
            ${store.ready(model) && html`<div>${model.prop1}</div>`}
            <div><button onclick="${toggleEdit}">Edit</button></div>
        `}
    `
})

image

message:

Property 'content' does not exist on type 'ISecondComponent'. ts(2339)

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "baseUrl": "./web",
    "paths": {
      // "/" is not "C:/", but is "./web"
      "/*": [ "*" ],
      // from module to their declaration
      "/modules/fingerprintjsv3.js": ["../node_modules/@fingerprintjs/fingerprintjs/dist/fp"],
      "https://esm.sh/mitt@3.0.1": ["../node_modules/mitt/index"],
      "https://esm.sh/just-intersect@4.3.0": [ "../node_modules/just-intersect/index" ],
      "https://esm.sh/bootstrap@5.3.2": ["../node_modules/@types/bootstrap/index"],
      "https://esm.sh/hybrids@8.2.11": ["@types/hybrids"],
      "https://esm.sh/colorjs.io@0.4.5": ["../node_modules/colorjs.io/types/src/index"],
      "https://esm.sh/ts-debounce@4.0.0": ["../node_modules/ts-debounce/dist/src/index"],
      "https://esm.sh/mezr@1.1.0": ["../node_modules/mezr/dist/esm/index"]
    },
    "skipLibCheck": true
  },
  "include": [ "./web" ]
}

typescript version: "5.1.6"

smalluban commented 5 months ago

I don't have time to check every TS error (from all of your issues) - can you confirm if the problem is solved by using TS v4?

FYI: https://github.com/hybridsjs/hybrids/issues/236#issuecomment-2009222885

Qsppl commented 5 months ago

Update: In the first case, this is my mistake. I shouldn't have written

function toggleEdit(host: ISecondComponent) {
    host.editMode = true
    host.draft = host.model
    host.content().querySelector('input').focus()
}

but

function toggleEdit(host: Component<ISecondComponent>) {
    host.editMode = true
    host.draft = host.model
    host.content?.(host).querySelector('input').focus()
}

result:

import { Model, define, html, store, Component } from "https://esm.sh/hybrids@8.2.11"

export interface IModel {
    prop1: number
}

export const ModelStore: Model<IModel> = {
    id: true,
    prop1: 1
}

function toggleEdit(host: Component<ISecondComponent>) {
    host.editMode = true
    host.draft = host.model
    host.content?.(host).querySelector('input').focus()
}

export interface ISecondComponent extends HTMLElement {
    model: IModel
    draft: IModel
    editMode: boolean
}

export default define<ISecondComponent>({
    tag: "a-second",
    model: store(ModelStore),
    draft: store(ModelStore, { draft: true }),
    editMode: {
        get: (host, lastValue) => !host.model || lastValue,
        set: (host, value, lastValue) => value,
    },
    content: ({ draft, model, editMode }) => html`
        ${editMode ? html`
            <form>
                <input value="${draft.prop1}" oninput="${html.set(draft, 'prop1')}" />
                <button type="submit">Save</button>
            </form>
        ` : html`
            ${store.ready(model) && html`<div>${model.prop1}</div>`}
            <div><button onclick="${toggleEdit}">Edit</button></div>
        `}
    `
})

With this fix on typescript 4.9.5, another error appears in the same place: image image

smalluban commented 5 months ago

Update your example with (it fixes the TS errors):

function toggleEdit(host: ISecondComponent) {
  host.editMode = true;
  host.draft = host.model;
  host.content().querySelector("input")?.focus();
}

export interface ISecondComponent extends HTMLElement {
  model: IModel;
  draft: IModel;
  editMode: boolean;
  content: () => ISecondComponent;
}

The argument of the toogleEdit is ISecondComponent, not Component<...> - this would be a definition, not an instance.

And if you want to use content or render directly, you must define those properties too in ISecondComponent, like you do for the rest of the properites.

Qsppl commented 5 months ago

Thank you. This solved the problem.