首页 > 解决方案 > 创建多个泛型类型的交集类型

问题描述

我一直在对可变参数类型和 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明确地采用了两个元组ThemeStyleAugmentationFunO & Partial<P1 & P2>

我希望能够编写一个 multiAugment 版本,它可以采用任意的 AugmentationFn 列表并将它们结合在一起,以便支持任何数量。例如,可能会返回一个版本的东西O & Partial<P1 & P2 & P3 ... & P100>

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

标签: typescriptgenerics

解决方案


我不能确定,因为我没有安装你正在使用的库,但也许你可以这样做:

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(在上面的例子中,我把它变成了一个休息参数而不是一个元组;TypeScript 不会从数组文字中推断出元组类型,但它会从一个rest 参数。如果你需要一个数组文字,你需要断言或以其他方式诱使编译器将其解释为元组)。

此类型参数SA是一个数组ThemedStyleAugmentationFn<any, any>。然后类型函数PfromSA<SA>使用条件类型中的类型推断来计算所需的P类型交集SA。并且 type 函数SAExtendsTProperly<SA>做了类似的事情来确保每个元素ThemedStyleAugmentationFn<P, TA>都有TA正确的扩展T(因为ThemedStyleAugmentationFn<P, TA>在 中可能是不变的TA,你不能只是确保SA extends ThemedStyleAugmentationFn<any, T>;你必须遍历元组并获取TA每个元素的 )。

应该可以按您的意愿工作,但同样,如果没有更多自包含代码,我无法确定。

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


推荐阅读