如何在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)