作为参考,我正在尝试解决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; };
}
string
和HTMLElement
有各自的重载,它们可以匹配但不能作为并集。这种情况下的解决方案是统一签名。我尝试了以下方法:
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 = HTMLElement
,JElement = never
和T = never
匹配。取而代之的是,看起来TElement | T
被从联合中淘汰了。
我猜测这可以通过条件类型解决,但是@types/jquery
当前以TypeScript 2.3为目标,因此我会首选一个不使用较新功能来保持兼容性的解决方案。 / p>
答案 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
分支应该不可访问。