如何通过TypeScript中的类型描述元组数组中元组元素之间的关系?

如何通过TypeScript中的类型描述元组数组中元组元素之间的关系?,typescript,generics,generic-programming,typescript-generics,Typescript,Generics,Generic Programming,Typescript Generics,我想通过TypeScript中的类型来描述元组数组中元组元素之间的关系 这可能吗 declare const str: string; declare const num: number; function acceptString(str: string) { } function acceptNumber(num: number) { } const arr /*: ??? */ = [ [str, acceptString], [num, acceptNumber],

我想通过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目前没有直接的支持。即使在您可以说服编译器在创建此类记录时捕获错误的情况下,它也无法在使用记录时验证类型安全性

实现此类类型的一种方法是使用哪个类型脚本。如果是这样的话,您可以将阵列描述为:

type MyRecord<T> = [T, (arg: T)=>void];
type SomeMyRecord = <exists T> MyRecord<T>; // this is not valid syntax
type ArrayOfMyRecords = Array<SomeMyRecord>;
这使用和。让我们看看它的实际行动:

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
。但是现在看看当你在上面迭代时会发生什么:

// TS3.3+ behavior
for (const pair of arr) {
  const [arg, func] = pair; 
  func(arg); // still error!
}
这就是缺乏对相关记录的支持的地方。在TypeScript 3.3中,添加了对调用的支持,但该支持没有涉及这个问题,即:编译器将
func
视为与
arg
类型完全不相关的函数的联合。当您调用它时,编译器决定它只能安全地接受类型为
string&number
的参数,而
arg
不是(也不是任何实际值,因为
string&number
折叠为
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
}
您的实际示例可以进行类似修改:

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();
}
函数creatify(arg:[new()=>T,new(x:T)=>U]){
返回Object.assign(arg,{create:()=>newarg[1](newarg[0]())});
}
常数映射={
[Type.Value1]:创建([Store1,Form1]),
[Type.Value2]:创建([Store2,Form2])
};
函数createForm(类型:type){
返回映射[type]。创建();
}
在TypeScript中模拟存在类型与上述类似,只是它允许您对
MyRecord
执行任何操作,如果您不知道
t
。因为在大多数情况下,这只是一个小的操作集,所以直接支持这些操作通常更容易



好的,希望能有帮助。祝你好运

不可能使其完全进行类型检查。您可以在
[str,acceptNumber]
上获得错误(需要函数和条件类型),但是
仍然需要类型assertion@TitianCernicova-德拉戈米尔谢谢你。这是TypeScript的一个基本限制,还是可以在将来解决?除了将数组中的每个元素作为参数调用其第二个元素之外,您打算如何处理这样一个数组?TS需要跟踪
arg
来自
pair
所以
func(arg)
是安全的,而像
func(“”
之类的东西则不是。虽然有可能追踪到这一点,但我认为在不久的将来我们不会看到这一点。@jcalz本例中没有任何内容。但现实世界的例子有点复杂(我添加了问题的链接)。我认为这两个例子是等价的。
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();
}