无法传递其类型可解决不同重载的联合类型

时间:2018-10-04 15:15:34

标签: typescript

作为参考,我正在尝试解决DefinitelyTyped/DefinitelyTyped#29312


以下代码会导致错误,但不应导致错误。

declare const $: JQueryStatic;

let value: string | HTMLElement = {} as any;
$(value);
Argument of type 'string | HTMLElement' is not assignable to parameter of type 'PlainObject<any>'.
  Type 'string' is not assignable to type 'PlainObject<any>'.

JQueryStatic的呼叫签名当前定义为:

interface JQueryStatic {
    <TElement extends HTMLElement = HTMLElement>(html: string, ownerDocument_attributes?: Document | JQuery.PlainObject): JQuery<TElement>;
    <TElement extends Element = HTMLElement>(selector: string, context?: Element | Document | JQuery): JQuery<TElement>;
    <T extends Element>(element: T): JQuery<T>;
    <T extends Element>(elementArray: T[]): JQuery<T>;
    <T>(selection: JQuery<T>): JQuery<T>;
    <TElement = HTMLElement>(callback: ((this: Document, $: JQueryStatic) => void)): JQuery<TElement>;
    <T extends JQuery.PlainObject>(object: T): JQuery<T>;
    <TElement = HTMLElement>(): JQuery<TElement>;
}

interface JQuery<T = HTMLElement> {
    jquery: string;
}

declare namespace JQuery {
    type PlainObject<T = any> = { [name: string]: T; };
}

stringHTMLElement有各自的重载,它们可以匹配但不能作为并集。这种情况下的解决方案是统一签名。我尝试了以下方法:

interface JQueryStatic2 {
    // This solution doesn't entirely fulfill the original requirements but
    // seems like a good starting point.
    <TElement extends HTMLElement = never,
        JElement = never,
        T extends JQuery.PlainObject = never>(
        arg: string |
            TElement |
            TElement[] |
            JQuery<JElement> |
            ((this: Document, $: JQueryStatic) => void) |
            T
    ): JQuery<TElement | JElement | T>;
}
Argument of type 'string | HTMLElement' is not assignable to parameter of type 'string | ((this: Document, $: JQueryStatic) => void) | JQuery<never> | never[]'.
  Type 'HTMLElement' is not assignable to type 'string | ((this: Document, $: JQueryStatic) => void) | JQuery<never> | never[]'.
    Type 'HTMLElement' is not assignable to type 'never[]'.
      Property 'length' is missing in type 'HTMLElement'.

我希望它与TElement = HTMLElementJElement = neverT = never匹配。取而代之的是,看起来TElement | T被从联合中淘汰了。

我猜测这可以通过条件类型解决,但是@types/jquery当前以TypeScript 2.3为目标,因此我会首选一个不使用较新功能来保持兼容性的解决方案。 / p>

1 个答案:

答案 0 :(得分:0)

我求助于使用条件类型(需要TypeScript 2.8)。

interface JQueryStatic {
    <TDeclared = JQuery._Unknown,
    T extends string | Element[] | JQuery<any> | Element | JQuery.PlainObject =
              string | Element[] | JQuery<any> | Element | JQuery.PlainObject,
    TReturn =
        TDeclared extends JQuery._Unknown ?
            T extends string ? HTMLElement :
            T extends Element[] | JQuery<any> ? T[number] :
            T extends Element | JQuery.PlainObject ? T :
            never :
        TDeclared>(html_selector_element_elementArray_object_selection: T): JQuery<TReturn>;
}

namespace JQuery {
    interface _Unknown {
        __unknown: never;
    }
}

这统一了许多原始签名,但取消了对现在TDeclared的限制。 T表示自变量的类型,并受原始签名可能的类型限制。如果声明TReturn等于TDeclared(即与哨兵类型JQuery._Unknown不兼容),否则根据T的类型进行推断。由于声明T的方式,never分支应该不可访问。