如何在TypeScript中定义泛型数组?

如何在TypeScript中定义泛型数组?,typescript,generics,Typescript,Generics,假设我有一个通用接口,如下所示: interface Transform<ArgType> { transformer: (input: string, arg: ArgType) => string; arg: ArgType; } 在本例中,为了让类型系统验证数组中的每个项是否满足通用的转换接口,我将常量转换定义为什么类型?(在下面使用TS 3.0) 如果直接支持TypeScript,我会告诉您使用它们。存在类型的意思是“我只知道类型存在,但我不知道或不关

假设我有一个通用接口,如下所示:

interface Transform<ArgType> {
    transformer: (input: string, arg: ArgType) => string;
    arg: ArgType;
}
在本例中,为了让类型系统验证数组中的每个项是否满足通用的
转换接口,我将
常量转换定义为什么类型?

(在下面使用TS 3.0)

如果直接支持TypeScript,我会告诉您使用它们。存在类型的意思是“我只知道类型存在,但我不知道或不关心它是什么。”然后您的
transforms
参数有一个类似
Array
您可以“放弃”,只需使用
数组字符串;arg:string;}[]”不能分配给类型为“从不”的参数。
“但至少这是一个错误


或者,您可能会意识到,如果您所做的只是将
arg
传递给
transformer
,那么您可以将您的存在类
SomeTransform
类型如下:

function append(input: string, arg: string): string {
    return input.concat(arg);
}

function repeat(input: string, arg: number): string {
    return input.repeat(arg);
}

const transforms = [
    {
        transformer: append,
        arg: " END"
    },
    {
        transformer: repeat,
        arg: 4
    },
];

function applyTransforms(input: string, transforms: \*what type goes here?*\): string {
    for (const transform of transforms) {
        input = transform.transformer(input, transform.arg);
    }

    return input;
}
applyTransforms("hey", [{transformer: repeat, arg: "oops"}]); // no error
interface SomeTransform {
  transformerWithArg: (input: string) => string;
}
并从所需的任何
变换
中进行
SomeTransform

const makeSome = <A>(transform: Transform<A>): SomeTransform => ({
  transformerWithArg: (input: string) => transform.transformer(input, transform.arg)
});
看看它是否有效:

const someTransforms = [
  makeSome({
    transformer: append,
    arg: " END"
  }),
  makeSome({
    transformer: repeat,
    arg: 4
  }),
];

applySomeTransforms("h", someTransforms);
如果你试图前后不一致:

makeSome({transformer: repeat, arg: "oops"}); // error
您会得到一个更合理的错误:“
参数'arg'和'arg'的类型不兼容。类型“string”不能分配给类型“number”。



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

你可以使用通用的元组rest参数(添加在TS 3.0中)来实现这一点

type TransformRest={
[P in keyof T]:T[P]扩展了T[number]?变换:从不
}
函数applyTransforms(输入:字符串,…转换:TransformRest):字符串{
for(变换的常量变换){
输入=transform.transformer(输入,transform.arg);
}
返回输入;
}
//从它的参数生成一个元组,否则typescript总是以数组的形式键入
函数元组(…参数:TS){
返回args;
}
//这样使用:
常量转换=元组(
{
变压器:附加,
arg:“结束”
},
{
变压器:重复,
arg:4
},
);
//并调用如下应用转换:
applyTransforms(“字符串”、…转换)
//或者像这样:
applyTransforms(“字符串”、transform1、transform2)
解释 Typescript具有非常强大的类型推断功能,但通常会选择它能选择的最松散的类型。在这种情况下,您需要强制它将您的转换视为一个元组,这样每个元素都有自己的类型,然后让推断完成其余的工作

我对映射类型进行了此操作,其中的一个问题是Typescript将使用所有元组键(例如“长度”),而不仅仅是数字键。您只需强制它仅映射数字键。因此条件:
T[P]扩展T[number]

我认为你不能用100%类型安全的方式来做这件事。
Transform在Typescript中需要多么复杂。这里
makeSome
的目的是什么?你只是从
Transform
创建一个类型为
SomeTransform
的新对象吗?在这种情况下,
someTransforms
的类型应该是是不是
SomeTransform[]
对吧?我想你可以把
VerifyTransform
简化为
type VerifyTransform=(T扩展Transform?未知:从不)
它似乎按预期工作。虽然
从不
是确保不兼容的逻辑方法,但字符串文字类型可能会产生更好的错误:
类型VerifyTransform=(T extends Transform?unknown:“要转换的参数不一致”)
@TitianCernicova Dragomir:这种简化可能会奏效……但我遇到了这样一个问题,
推断出一个
最终会推断出一个类型的交集(因此是两步过程),或者联合体中的任何一个类型都能使整个过程奏效,而我们需要所有类型都能奏效(因此“永不未知”和“再次返回”的东西使一个失败给出所有失败)。我希望这个失败:
applyTransforms(“嘿”,[{transformer:repeat,arg:1},{transformer:repeat,arg:oops}])
@Niladri,是的,这就是我正在做的。巧妙地使用分布条件类型来验证
变换的推断类型是否为
变换|变换|…
!当然,这种方法有一些局限性。它没有为
数组提供一种可以编写的类型。Transform>
,这样您就不会自动检查操作的类型,比如将转换附加到现有数组中。
interface SomeTransform {
  transformerWithArg: (input: string) => string;
}
const makeSome = <A>(transform: Transform<A>): SomeTransform => ({
  transformerWithArg: (input: string) => transform.transformer(input, transform.arg)
});
function applySomeTransforms(input: string, transforms: SomeTransform[]): string {
  for (const someTransform of transforms) {
    input = someTransform.transformerWithArg(input);
  }

  return input;
}
const someTransforms = [
  makeSome({
    transformer: append,
    arg: " END"
  }),
  makeSome({
    transformer: repeat,
    arg: 4
  }),
];

applySomeTransforms("h", someTransforms);
makeSome({transformer: repeat, arg: "oops"}); // error
type TransformRest<T extends any[]> = {
   [P in keyof T]: T[P] extends T[number] ? Transform<T[P]> : never
}

function applyTransforms<T extends any[]>(input: string, ...transforms: TransformRest<T>): string {
   for (const transform of transforms) {
      input = transform.transformer(input, transform.arg);
   }

   return input;
}

// Makes a tuple from it's arguments, otherwise typescript always types as array
function tuplify<TS extends any[]>(...args: TS) {
   return args;
}

// Use like this:
const transforms = tuplify(
   {
      transformer: append,
      arg: " END"
   },
   {
      transformer: repeat,
      arg: 4
   },
);

//And call apply transforms like this:
applyTransforms("string", ...transforms)

//or like this:
applyTransforms("string", transform1, transform2)