[英]How do you define an array of generics in TypeScript?
假設我有一個如下通用界面:
interface Transform<ArgType> {
transformer: (input: string, arg: ArgType) => string;
arg: ArgType;
}
然后我想將這些Transform
的數組應用於string
。 如何定義此Transform
數組,以便驗證<ArgType>
在Transform.transformer
和Transform.arg
是否相同? 我想寫這樣的東西:
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;
}
在這個例子中,我定義了const transforms
的類型,以便類型系統驗證數組中的每個項是否滿足泛型Transform<ArgType>
接口?
(以下使用TS 3.0)
如果TypeScript直接支持存在類型 ,我會告訴你使用它們。 存在主義類型意味着“我所知道的是類型存在,但我不知道或不關心它是什么。” 然后你的transforms
參數有一個像Array< exists A. Transform<A> >
這樣的類型,意思是“對於某些 A
來說是一個Transform<A>
的數組”。 有一個建議允許這些類型的語言,但很少有語言支持這個誰知道。
您可以“放棄”並使用Array<Transform<any>>
,這將起作用但無法捕獲這樣的不一致情況:
applyTransforms("hey", [{transformer: repeat, arg: "oops"}]); // no error
但正如你所說,即使在沒有存在類型的情況下,你也希望強化一致性。 幸運的是,有一些變通方法具有不同程度的變通方法。 這是一個:
讓我們來聲明一個類函數,它接受一個T
的,如果它Transform<A>
對一些 A
,它返回unknown
(新的頂級型 ,其每一個值相匹配......所以unknown & T
等於T
所有T
)否則它會返回never
(不匹配任何值的底部類型 ......所以never & T
等於所有T
never
):
type VerifyTransform<T> = unknown extends
(T extends { transformer: (input: string, arg: infer A) => string } ?
T extends { arg: A } ? never : unknown : unknown
) ? never : unknown
它使用條件類型來計算它。 這個想法是它看着transformer
找出A
,然后確保arg
與那個A
兼容。
現在我們可以輸入applyTransforms
作為泛型函數,它只接受一個transforms
參數,該參數匹配類型為T
的元素匹配VerifyTransform<T>
的數組:
function applyTransforms<T extends Transform<any>>(
input: string,
transforms: Array<T> & VerifyTransform<T>
): string {
for (const transform of transforms) {
input = transform.transformer(input, transform.arg);
}
return input;
}
在這里,我們看到它工作:
applyTransforms("hey", transforms); // okay
如果傳入不一致的內容,則會出現錯誤:
applyTransforms("hey", [{transformer: repeat, arg: "oops"}]); // error
錯誤並不是特別有啟發性:“ [ts] Argument of type '{ transformer: (input: string, arg: number) => string; arg: string; }[]' is not assignable to parameter of type 'never'.
“但至少這是一個錯誤。
或者,你可以意識到,如果你所做的只是將arg
傳遞給transformer
,你可以像這樣SomeTransform
類似於存在主義的SomeTransform
類型:
interface SomeTransform {
transformerWithArg: (input: string) => string;
}
並根據您想要的任何Transform<A>
制作SomeTransform
:
const makeSome = <A>(transform: Transform<A>): SomeTransform => ({
transformerWithArg: (input: string) => transform.transformer(input, transform.arg)
});
然后接受一個SomeTransform
數組:
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
你得到一個更合理的錯誤:“ Types of parameters 'arg' and 'arg' are incompatible. Type 'string' is not assignable to type 'number'.
”
好的,希望有所幫助。 祝好運。
您可以使用通用元組休息參數(在TS 3.0中添加)來執行此操作。
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)
Typescript具有非常強大的類型推斷,但通常選擇最寬松的類型。 在這種情況下,您需要強制它將您的變換視為元組,以便每個元素都有自己的類型,然后讓推理完成剩下的工作。
我用映射類型做了這個,對此的一個打嗝是Typescript將使用所有元組鍵(例如“length”),而不僅僅是數字鍵。 你只需要強制它只映射數字。 因此條件: T[P] extends T[number]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.