我使用d3来自 的类型定义@types/d3。我有一种结合选择和转换的方法。该参数可以是其中之一。Selection 和 Transition 类型都有该selectAll方法。selectAll但是当我尝试向工会申请时,出现以下错误:
该表达式不可调用。联合体类型的每个成员 '{(): Selection<null, undefined, BaseType,unknown>; (选择器:null):Selection<null,未定义,BaseType,未知>;(选择器:未定义):Selection<...>;<DescElement extends BaseType, OldDatum>(选择器:字符串):Selection<...>;<DescElement extends BaseType, OldDatum>(选择器:ValueFn<...>):Selection<...>;} ...' 有签名,但这些签名都不相互兼容。
以下是给出此错误的代码示例:
/* Definitions from @types/d3 */
export type BaseType = Element | Document | Window | null;
export type ValueFn<T extends BaseType, Datum, Result> = (this: T, datum: Datum, index: number, groups: T[] | ArrayLike<T>) => Result;
export interface Transition<GElement extends BaseType, Datum, PElement extends BaseType, PDatum> {
selectAll<DescElement extends BaseType, OldDatum>(selector: string): Transition<DescElement, OldDatum, GElement, Datum>;
selectAll<DescElement extends BaseType, OldDatum>(selector: ValueFn<GElement, Datum, DescElement[] | ArrayLike<DescElement>>): Transition<DescElement, OldDatum, GElement, Datum>;
}
export interface Selection<GElement extends BaseType, Datum, PElement extends BaseType, PDatum> {
selectAll(): Selection<null, undefined, GElement, Datum>;
selectAll(selector: null): Selection<null, undefined, GElement, Datum>;
selectAll(selector: undefined): Selection<null, undefined, GElement, Datum>;
selectAll<DescElement extends BaseType, OldDatum>(selector: string): Selection<DescElement, OldDatum, GElement, Datum>;
selectAll<DescElement extends BaseType, OldDatum>(selector: ValueFn<GElement, Datum, DescElement[] | ArrayLike<DescElement>>): Selection<DescElement, OldDatum, GElement, Datum>;
}
/* My code */
function myFunc(maybeTransition: Selection<BaseType, unknown, BaseType, unknown> | Transition<BaseType, unknown, BaseType, unknown>) {
const texts = maybeTransition.selectAll('text'); // ERROR: This expression is not callable.
// do something with texts
}
Run Code Online (Sandbox Code Playgroud)
所以问题是:
对于函数的参数,maybeTransition您指定一个联合类型,希望它正是这两种类型的联合:
Selection<BaseType, unknown, BaseType, unknown> | Transition<BaseType, unknown, BaseType, unknown>\nRun Code Online (Sandbox Code Playgroud)\n不幸的是,完全违反直觉的是,联合类型的行为与您的预期完全不同!文档告诉我们
\n\n\n联合类型是由两个或多个其他类型形成的类型,表示可以是任意一个的值类型的值。
\n
到目前为止,一切都很好。然而,当谈到联合类型的属性时
\n\n\n仅当操作对联合体的每个成员都有效时,TypeScript 才会允许该操作。
\n
如果您将接口/类型视为属性集,从数学上讲,这将是一个交集而不是并集。我一直认为这是用词不当。文档中的小字也提到了这一事实:
\n\n\n类型的联合似乎具有这些 types\xe2\x80\x99 属性的交集,这可能会令人困惑。这并非偶然——union 这个名字来自于类型理论。并集是通过取每种类型的 值的
\nnumber | string并集而组成的。请注意,给定两个集合以及每个集合的相应事实,只有这些事实的交集适用于集合本身的并集。例如,如果我们有一个房间,里面都是戴着帽子的高个子,另一个房间里讲西班牙语的人都戴着帽子,那么将这些房间组合起来后,我们对每个人唯一了解的就是他们一定戴着帽子。
查看 的类型定义Selection,Transition您会注意到这两种类型中都没有兼容的方法,从而导致您得到一个完全没有属性的空类型。因此,无法调用方法.selectAll()在该空类型上调用方法,这基本上就是错误消息告诉您的内容。
有两种方法可以解决这个问题:
\n使用交叉点类型。您可以将类型定义修改为
\n Selection<BaseType, unknown, BaseType, unknown> & Transition<BaseType, unknown, BaseType, unknown>\nRun Code Online (Sandbox Code Playgroud)\n虽然这会起作用,但我不鼓励使用,因为您将两个不同的接口混合成一种类型以“使其起作用”。您没有扩展接口,也没有丰富它,而只是混合了重载的定义.selectAll()这感觉有点粗略。
Selection或 a Transition。这可以通过使用类型保护来完成instanceof。这样,TypeScript 就能够区分这两种类型:import { Selection, selection, Transition, transition, BaseType } from "d3";\n\nfunction myFunc(\n maybeTransition: Selection<BaseType, unknown, BaseType, unknown> |\n Transition<BaseType, unknown, BaseType, unknown>\n) {\n let texts;\n if (maybeTransition instanceof selection) { // type guard\n texts = maybeTransition.selectAll("text"); // texts is of type Selection\n } else if (maybeTransition instanceof transition) { // type guard\n texts = maybeTransition.selectAll("text"); // texts is of type Transition\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这可能看起来很笨拙,但这是由于 TypeScript 本身的类型安全性造成的。虽然两者Selection以及TransitionVanilla JavaScript 中的模型看起来很相似,但 TypeScript 的类型安全有时会以更冗长的代码为代价。
您还必须记住,上述所有内容都适用于texts变量,该变量也是两种类型的联合。稍后您将必须在代码中以相同的方式处理该问题。不过,根据您的整体设计,可能值得考虑采用完全不同的方法。
| 归档时间: |
|
| 查看次数: |
608 次 |
| 最近记录: |