如何键入我的create
函数,以便它可以安全地推断接收到的每一对的单个类型?
type Component<T=any> = { id: string, _type?: T };
type Pair<T=any> = [Component<T>, T];
function create<T extends Pair[]>(...pairs: T) {
// ...
}
type Vector = [number, number];
type Status = "active" | "idle";
let Position: Component<Vector> = { id: "position" };
let Status: Component<Status> = { id: "status" };
let entity = create(
[Position, [0, 0]],
[Status, false], // should fail as it expects `Status` and got `boolean`
);
我可以通过显式传递type参数来实现所需的行为。
let entity = create<[
Pair<Vector>,
Pair<Status>,
]>(
[Position, [0, 0]],
[Status, false], // should fail as it expects `Status` and got `boolean`
);
我的直觉是,一旦编译器已经推断出Pair
的类型为pairs
,就不会尝试从其元素中推断嵌套的Pair[]
类型。
答案 0 :(得分:1)
您在{{1}中的rest parameter ...pairs
中的所有项目都打包为一种扩展了create
的通用类型(其中Pair[]
被解析为T
默认情况)。编译器将给定的参数解释为数组,并且数组中的项目类型相同,因此这里没有机会对没有类型注释的单个项目进行类型检查。
一种替代方法是您通过手动输入any
的泛型类型参数或将函数参数与函数调用分开来提供的示例:
create
当要在const t: [Pair<Vector>, Pair<Status>] = [[Position, [0, 0]], [Status, false]]; // error (OK)
let entity = create(...t);
中传递固定数量的Pair
项时,或者所有create
项的复合类型已知时,此解决方案是可行的。如果无法实现和/或以更动态的方式创建数组,则可以使用一些辅助函数Pair
,其单个目的是强制执行asPair
的强类型,如下所示:
Pair
答案 1 :(得分:1)
在TS中,在数组项内部获取这种相关性不是很简单。如另一个答案所述,一种解决方案是将一对专用功能配对,并在每个项目上调用该功能。尽管此解决方案有效,但它并不理想,因为它迫使用户记住选择加入此检查。
另一种解决方案是使用自定义方式执行检查。这取决于以下条件:
如果参数是包含类型参数和依赖该类型参数的第二种类型(例如:T & SomeMappedType<T>
)的交集,则类型脚本将推断类型参数T
并将在其中使用推断的类型SomeMappedType<T>
并对照SomeMappedType<T>
检查参数。基本上,我们捕获了T
中参数的实际类型,但是对依赖于T
的单独类型执行检查。
对于本示例来说,这意味着我们将使用类型参数将数组的实际类型捕获为元组(在您的示例中为[[Component<Vector>, number[]], [Component<Status>, boolean]]
),然后使用此类型来构建我们要实际上要检查(在此示例中,应该为[Pair<Vector>, Pair<Status>]
)。我们将使用简单的映射类型来做到这一点。
type Component<T> = { id: string, usage?: T };
type Pair<T> = [Component<T>, T];
type PairCheck<T extends Pair<any>[]> = {
[P in keyof T]: T[P] extends [Component<infer U>, any] ? Pair<U>: never
}
function create<T extends Pair<any>[]>(...pairs: T & PairCheck<T>) {
// ...
}
type Vector = [number, number];
type Status = "active" | "idle";
let Position: Component<Vector> = { id: "position" };
let Status: Component<Status> = { id: "status" };
let entity = create(
[Position, [0, 0]],
[Status, true],
);
注意:Component
必须使用T
才能使其中任何一项正常工作。阅读FAQ,了解未使用的类型参数。
您得到的错误是
Argument of type '[[Component<Vector>, [number, number]], [Component<Status>, boolean]]' is not assignable to parameter of type '[[Component<Vector>, number[]], [Component<Status>, boolean]] & [Pair<Vector>, Pair<Status>]'.
Type '[[Component<Vector>, [number, number]], [Component<Status>, boolean]]' is not assignable to type '[Pair<Vector>, Pair<Status>]'.
Type '[Component<Status>, boolean]' is not assignable to type 'Pair<Status>'.
Type 'boolean' is not assignable to type 'Status'
哪一个是令人难以置信的,错误跨度是第一个元素而不是令人讨厌的元素(它实际上可能是编译器错误),但是它确实清楚了IMO的错误。