Skip to content

createCqlInput() return type is incompatible with CustomElementConstructor (TS2345) #119

@paperboyo

Description

@paperboyo

Hi. Sorry, my helpful robot insists it found something. I obviously don't have a clue. Please excuse me and close if it's shrooms...

Severity: High — blocks customElements.define() without a cast.

Error:

Argument of type '{ new (): { editorView: EditorView | undefined; ... }; observedAttributes: string[]; }'
is not assignable to parameter of type 'CustomElementConstructor'.
  Type '...' is missing the following properties from type 'HTMLElement':
    autocorrect, ariaActiveDescendantElement, ariaControlsElements,
    ariaDescribedByElements, and 7 more.

Cause:
createCqlInput() is declared as returning an anonymous { new(): { … } } object type, where the instance type is an inline expansion of HTMLElement. TypeScript 5.8's DOM lib (lib.dom.d.ts) added new properties to HTMLElement (autocorrect, ariaActiveDescendantElement, ariaControlsElements, ariaDescribedByElements, ariaDetailsElements, ariaErrorMessageElements, ariaFlowToElements, ariaLabelledByElements, ariaOwnsElements, editContext, commandForElement). The CQL package was built with TypeScript 5.6, so its expanded type snapshot is missing these.

Proposed fix:
Change createCqlInput to return a named class type that extends HTMLElement, instead of an inline structural expansion. This way, the return type is typeof CqlInputElement (a proper subclass), and consumers' lib.dom.d.ts supplies the full HTMLElement surface — no snapshot staleness.

// Before (current — CqlInput.ts)
export const createCqlInput = (typeahead: Typeahead, config?: CqlConfig) => {
  return class CqlInputElement extends HTMLElement {
    // ...
  };
};
// The emitted .d.ts inlines every HTMLElement property into the return type.

// After — explicitly annotate the return type
export const createCqlInput = (
  typeahead: Typeahead,
  config?: CqlConfig
): CustomElementConstructor => {
  return class CqlInputElement extends HTMLElement {
    // ...
  };
};

Alternatively, if CQL-specific instance members (.value, .editorView, etc.) should remain visible to consumers:

export class CqlInputBase extends HTMLElement {
  editorView: EditorView | undefined;
  value!: string;
  // ...other CQL-specific members
}

export const createCqlInput = (
  typeahead: Typeahead,
  config?: CqlConfig
): typeof CqlInputBase => {
  return class CqlInputElement extends CqlInputBase {
    // ...
  };
};

This way TypeScript emits typeof CqlInputBase (a reference), not an inline expansion — immune to DOM lib drift.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions