TypeScript - '元素'不能分配给HTMLInputElement

时间:2017-11-09 09:36:43

标签: javascript reactjs typescript types

我正在使用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();
}

此错误与Element类型没有focus()方法有关。

问题

有没有办法告诉TypeScript document.activeElement应该是输入类型?像

这样的东西

this.focusedElementBeforeDialogOpened = <HTMLInputElement>document.activeElement;

或者是否有更好的解决方法,因此声明支持document.activeElement.focus()的类型?

2 个答案:

答案 0 :(得分:1)

来自document.activeElement的文档:

  

如果此对象具有文本选择,通常会返回<input><textarea>对象。如果是这样,您可以使用元素的selectionStart和selectionEnd属性获得更多细节。其他时候,focus元素可能是<select>元素(菜单)或<input>元素,类型为button,checkbox或radio。

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

如果您确实只是在寻找HTMLInputElement,则可以使用TypeScript识别的简单instanceof类型保护:

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;

答案 1 :(得分:0)

实际上,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();
    }
}