【问题标题】:TypeScript - 'Element' is not assignable to HTMLInputElementTypeScript - “元素”不可分配给 HTMLInputElement
【发布时间】:2018-04-22 05:47:34
【问题描述】:

我正在使用 TypeScript 和 React。在我的组件(一个对话框窗口)中,我想将触发元素(例如按钮)存储为属性。当组件卸载时,我想将焦点返回到该元素。但是,我收到来自 TSLint 的错误,我不确定如何解决。

class Dialog extends React.Component<Props, {}> {
  ...
  focusedElementBeforeDialogOpened: HTMLInputElement;

  componentDidMount() {
    this.focusedElementBeforeDialogOpened = document.activeElement;
    ...
  }
  ...
}

我在分配属性值的那一行收到错误:

[ts] Type 'Element' is not assignable to type 'HTMLInputElement'.
  Property 'accept' is missing in type 'Element'.

但是,如果我将属性的类型更改为Element 甚至HTMLInputElement,我会在componentWillUnmount() 中收到错误

componentWillUnmount() {
    ...
    this.focusedElementBeforeDialogOpened.focus();
}

此错误与没有 focus() 方法的元素类型有关。

问题

有没有办法告诉 TypeScript document.activeElement 应该是输入类型?类似的东西

this.focusedElementBeforeDialogOpened = &lt;HTMLInputElement&gt;document.activeElement;

或者有没有更好的解决方法,所以声明支持document.activeElement.focus()的类型?

【问题讨论】:

  • 我对 React 不熟悉,但在 Angular 中,你有 nativeElement 属性。 `activeElement` 后面没有吗?

标签: javascript reactjs typescript types


【解决方案1】:

来自document.activeElement的文档:

如果当时有文本选择,这通常会返回一个&lt;input&gt;&lt;textarea&gt; 对象。如果是这样,您可以使用元素的 selectionStart 和 selectionEnd 属性获得更多详细信息。其他时候,焦点元素可能是&lt;select&gt; 元素(菜单)或&lt;input&gt; 元素,类型为按钮、复选框或单选。

也就是说,document.activeElement 不一定是HTMLInputElement 的实例(也可以是HTMLSelectElement 等)。

如果你确实只是在寻找一个HTMLInputElement,你可以使用一个简单的instanceof type-guard,它可以被 TypeScript 识别:

componentDidMount() {
    if (document.activeElement instanceof HTMLInputElement) {
        this.focusedElementBeforeDialogOpened = document.activeElement;
    }
    ...
}

此外,您可以检测元素是否可聚焦,例如通过定义你自己的类型保护:

interface IFocusableElement {
    focus(): void;
}

function isFocusable(element: any): element is IFocusableElement {
    return (typeof element.focus === "function");
}

然后使用IFocusableElement 作为this.focusedElementBeforeDialogOpened 的类型和你自己的类型保护:

focusedElementBeforeDialogOpened: IFocusableElement;

componentDidMount() {
    if (document.activeElement && isFocusable(document.activeElement)) {
        this.focusedElementBeforeDialogOpened = document.activeElement;
    }
    ...
}

如果您还需要Element提供的原始API,您可以使用交集类型:

focusedElementBeforeDialogOpened: IFocusableElement & Element;

【讨论】:

  • 既然 HTMLInputElement 似乎只返回 是否建议将 HTMLButtonElement 也添加到可能的类型和类型保护中以支持
  • 这个解决方案有一些优雅的部分,但并不完整,因为当document.activeElement 没有focus 方法时,componentWillUnmount() 可以在undefined 上调用this.focusedElementBeforeDialogOpened.focus();
【解决方案2】:

确实,lib.es6.d.ts 中的定义有点不一致,除非是 W3C 规范有点混乱:

interface Document extends [...] {
    /**
     * Gets the object that has the focus when the parent document has focus.
     */
    readonly activeElement: Element;
    [...]
    focus(): void;
    [...]
}

interface HTMLElement extends Element {
    [...]
    focus(): void;
    [...]
}

所以,我们可以手动对焦的唯一两种对象是DocumentHtmlElement,但任何Element 都可以对焦!

如果你只需要交还焦点,你可以这样做:

class Dialog extends React.Component<Props, {}> {
    private resetFocus = () => {};

    componentDidMount() {
        const elementToFocus = document.activeElement instanceof HTMLElement
            ? document.activeElement
            : document;
        this.resetFocus = () => elementToFocus.focus();
    }

    componentWillUnmount() {
        this.resetFocus();
    }
}

componentDidMount() {
    const activeElement = document.activeElement as any;
    if (typeof activeElement.focus === 'function') {
        this.resetFocus = () => activeElement.focus();
    }
}

【讨论】:

  • 感谢那里的一些文档。归还焦点不是我关心的问题。我试图找到如何避免使用类型“任何”。
  • 我刚刚编辑了代码以删除不必要的any。它可能更适合您的需求^^
猜你喜欢
  • 2020-07-29
  • 1970-01-01
  • 2023-03-30
  • 2018-07-07
  • 2021-03-10
  • 1970-01-01
  • 2019-07-14
  • 2021-06-30
  • 2019-03-19
相关资源
最近更新 更多