如何通过TypeScript中的类型描述元组数组中元组元素之间的关系? [英] How to describe the relationship between elements of tuple in array of tuples via types in TypeScript?
问题描述
我想通过TypeScript中的类型描述元组数组中元组元素之间的关系.
I want to describe the relationship between elements of tuple in array of tuples via types in TypeScript.
这可能吗?
declare const str: string;
declare const num: number;
function acceptString(str: string) { }
function acceptNumber(num: number) { }
const arr /*: ??? */ = [
[str, acceptString],
[num, acceptNumber],
[str, acceptNumber], // should be error
];
for (const pair of arr) {
const [arg, func] = pair;
func(arg); // should be no error
}
真实示例:推荐答案
您基本上是在要求我打电话给我的东西相关的记录类型,目前在TypeScript中尚无直接支持.即使您可以说服编译器在创建此类记录时发现错误,也无法真正使用它来验证类型安全性. You're basically asking for something I've been calling correlated record types, for which there is currently no direct support in TypeScript. Even in the case where you can convince the compiler to catch errors in creating such records, it is not really equipped to verify type safety when using one. 实现此类类型的一种方法是使用存在定量的泛型,其中TypeScript 当前不直接支持.如果是这样,您可以将数组描述为: One way to implement such types would be with existentially quantified generics which TypeScript does not currently support directly. If it did, you'd be able to describe your array as something like: 下一个最好的办法是允许 The next best thing might be to allow This uses infererence from mapped types and mapped tuples. Let's see it in action: 让我们解决这个问题: 因此足以定义 So that works well enough to define 这是缺少相关记录支持的地方,这会伤到您.在TypeScript 3.3中,添加了对调用函数类型的联合的支持,但是该支持确实可以不能解决这个问题,即:编译器将 This is where the lack of support for correlated records burns you. In TypeScript 3.3, support was added for calling unions of function types, but that support does not touch this issue, which is: the compiler treats So if you go this way you'll find you need a type assertion to calm the compiler down: 一个人可能会认为这是您可以做的最好的事情,然后把它留在那里. One might decide this is the best you can do and to leave it there. 现在,有一种方法可以在TypeScript中编码存在类型,但是它涉及类似于 Now, there is a way to encode existential types in TypeScript, but it involves a 您的真实示例可以进行类似的修改: Your real-world example could be modified similarly: 在TypeScript中模拟存在性类型与上述类似,不同之处在于它允许您对 Emulating existential types in TypeScript is similar to the above except that it allows you to do absolutely anything to a 好的,希望能有所帮助.祝你好运! Okay, hope that helps. Good luck! 这篇关于如何通过TypeScript中的类型描述元组数组中元组元素之间的关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!type MyRecord<T> = [T, (arg: T)=>void];
type SomeMyRecord = <exists T> MyRecord<T>; // this is not valid syntax
type ArrayOfMyRecords = Array<SomeMyRecord>;
ArrayOfMyRecords
本身是一种通用类型,其中,数组的每个元素都使用类似的T
值进行强类型化,并使用一个辅助函数来推断更强的类型:>
ArrayOfMyRecords
to itself be a generic type in which each element of the array is strongly typed with its analogous T
value, and a helper function to infer the stronger type:type MyRecord<T> = [T, (arg: T) => void];
const asMyRecordArray = <A extends any[]>(
a: { [I in keyof A]: MyRecord<A[I]> } | []
) => a as { [I in keyof A]: MyRecord<A[I]> };
const arr = asMyRecordArray([
[str, acceptString],
[num, acceptNumber],
[str, acceptNumber] // error
]);
// inferred type of arr:
// const arr: [
// [string, (arg: string) => void],
// [number, (arg: number) => void],
// [string, (arg: string) => void]
// ]
const arr = asMyRecordArray([
[str, acceptString],
[num, acceptNumber],
[str, acceptString]
]);
// inferred type of arr:
// const arr: [
// [string, (arg: string) => void],
// [number, (arg: number) => void],
// [string, (arg: string) => void]
// ]
arr
.但是,现在看看当您对其进行迭代时会发生什么:arr
. But now look what happens when you iterate over it:// TS3.3+ behavior
for (const pair of arr) {
const [arg, func] = pair;
func(arg); // still error!
}
func
视为函数的 union ,这些函数与string & number
的参数,而arg
不是(也不是任何实际值,因为string & number
折叠为never
).func
as a union of functions which is completely uncorrelated with the type of arg
. When you call it, the compiler decides that it can only safely accept arguments of type string & number
, which arg
is not (nor is any actual value, since string & number
collapses to never
).for (const pair of arr) {
const [arg, func] = pair as MyRecord<string | number>;
func(arg); // no error now
func(12345); // no error here either, so not safe
}
Promise
的控件反转.不过,在走那条路线之前,请问问自己:当您不知道T
时,您将如何实际使用MyRecord<T>
?您可以做的唯一合理的事情是将其第一个元素称为第二个元素.如果是这样,您可以给出一种更具体的方法,只需做到这一点而无需跟踪T
:Promise
-like inversion of control. Before we go down that route, though, ask yourself: what are you going to actually do with a MyRecord<T>
when you don't know T
? The only reasonable thing you can do is to call its first element with its second element. And if so, you can give a more concrete method that just does that without keeping track of T
:type MyRecord<T> = [T, (arg: T) => void];
type MyUsefulRecord<T> = MyRecord<T> & { callFuncWithArg(): void };
function makeUseful<T>(arg: MyRecord<T>): MyUsefulRecord<T> {
return Object.assign(arg, { callFuncWithArg: () => arg[1](arg[0]) });
}
const asMyUsefulRecordArray = <A extends any[]>(
a: { [I in keyof A]: MyUsefulRecord<A[I]> } | []
) => a as { [I in keyof A]: MyUsefulRecord<A[I]> };
const arr = asMyUsefulRecordArray([
makeUseful([str, acceptString]),
makeUseful([num, acceptNumber]),
makeUseful([str, acceptString])
]);
for (const pair of arr) {
pair.callFuncWithArg(); // okay!
}
function creatify<T, U>(arg: [new () => T, new (x: T) => U]) {
return Object.assign(arg, { create: () => new arg[1](new arg[0]()) });
}
const map = {
[Type.Value1]: creatify([Store1, Form1]),
[Type.Value2]: creatify([Store2, Form2])
};
function createForm(type: Type) {
return map[type].create();
}
MyRecord<T>
进行绝对任何操作,如果您不知道T
则可以执行.由于在大多数情况下,这只是一小部分操作,所以直接直接支持这些操作通常会更容易.MyRecord<T>
that can be done if you don't know T
. Since in most cases this is a small set of operations, it's often easier to just support those directly instead.