创建多个通用类型的相交类型

时间:2019-01-08 00:12:31

标签: typescript generics

我一直在大量阅读TypeScript中的可变参数类型和一些新的元组内容,但是由于当前的语言功能,我无法确定我要完成的工作是否完全不可能。

有关API的确切问题,请参见此处:

export type Themed<T> = { theme: T };

export type ThemedStyleAugmentationFn<P, T> = (
  allProps: Partial<P> & Themed<T>
) => string;

export function augmentComponent<
  P,
  TA extends T,
  C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
  T extends object,
  O extends object = {},
  A extends keyof any = never
>(
  styledComponent: StyledComponent<C, T, O, A>,
  styleAugmentation: ThemedStyleAugmentationFn<P, TA>
): StyledComponent<C, T, O & Partial<P>, A> {
  const augmented = styled(styledComponent)`
    ${styleAugmentation}
  `;

  return augmented as StyledComponent<C, T, O & Partial<P>, A>;
}

export function multiAugment<
  P1 extends {},
  TA1 extends T,
  P2 extends {},
  TA2 extends T,
  C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
  T extends object,
  O extends object = {},
  A extends keyof any = never
>(
  styledComponent: StyledComponent<C, T, O, A>,
  styleAugmentations: [ThemedStyleAugmentationFn<P1, TA1>, ThemedStyleAugmentationFn<P2, TA2>]
): StyledComponent<C, T, O & Partial<P1 & P2>, A> {
  const augmented = styled(styledComponent)`
    ${styleAugmentations}
  `;

  return augmented as StyledComponent<C, T, O & Partial<P1 & P2>, A>;
}

augmentComponent中,签名的重要部分在这里:O & Partial<P>

multiAugment中,它显式地包含两个ThemeStyleAugmentationFun的元组,它在这里:O & Partial<P1 & P2>

我希望能够编写一个multiAugment版本,该版本可以获取AugmentationFn的任意列表并将它们合并在一起,从而可以支持任何arity。例如,可能会返回O & Partial<P1 & P2 & P3 ... & P100>版本的内容。

我想避免(如果可能)需要大量的函数重载来支持这一点,因为我可以继续编写长度不断增加的Tupled版本。

1 个答案:

答案 0 :(得分:4)

我不确定,因为我没有安装您正在使用的库,但是也许您可以执行以下操作:

type PfromSA<SA extends Array<ThemedStyleAugmentationFn<any, any>>>
  = { [K in keyof SA]: SA[K] extends ThemedStyleAugmentationFn<infer P, any> ? (x: P) => void : never } extends
  { [k: number]: (x: infer P) => void } ? P : never;

type SAExtendsTProperly<SA extends Array<ThemedStyleAugmentationFn<any, any>>, T>
  = { [K in keyof SA]: SA[K] extends ThemedStyleAugmentationFn<any, infer U> ? [U] extends [T] ? SA[K] : ThemedStyleAugmentationFn<any, T> : never }

export function multiAugment<
  SA extends Array<ThemedStyleAugmentationFn<any, any>>,
  C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
  T extends object,
  O extends object = {},
  A extends keyof any = never
>(
  styledComponent: StyledComponent<C, T, O, A>,
  ...styleAugmentations: SA & SAExtendsTProperly<SA, T>
): StyledComponent<C, T, O & Partial<PfromSA<SA>>, A> {
  const augmented = styled(styledComponent)`
    ${styleAugmentations}
  `;

  return augmented as StyledComponent<C, T, O & Partial<PfromSA<SA>>, A>;
}

这个想法是,您为styleAugmentations参数使用了generic rest parameter(在上面的示例中,我将其设置为rest参数而不是元组; TypeScript不会从数组文字中推断出元组类型,但会从rest参数推断出元组类型。如果您需要数组文字,则需要声明或以其他方式诱使编译器将其解释为元组。

此类型参数SAThemedStyleAugmentationFn<any, any>的数组。然后,类型函数PfromSA<SA>使用type inference in conditional typesP计算出所需的SA类型的交集。类型函数SAExtendsTProperly<SA>的作用类似于确保每个元素ThemedStyleAugmentationFn<P, TA>的{​​{1}}正确扩展了TA(因为T在{{1 }},您不能仅仅确保ThemedStyleAugmentationFn<P, TA>;您必须遍历元组并为每个元素抓住TA

应该可以根据您的需要工作,但是再次,我不确定没有更多独立的代码。

希望对您有帮助。祝你好运。