簡體   English   中英

如何在TypeScript中定義泛型數組?

[英]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.transformerTransform.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM