用于向MUI样式化实用程序提供样式的Typescript类型定义

vmdwslir  于 2022-11-26  发布在  TypeScript
关注(0)|答案(1)|浏览(147)

我在MUI v5中创建了一系列样式化的组件,这些组件上定义了一些额外的属性。

const SomeContainer = styled(Box, {
    name: "SomeContainer",
    slot: "Root"
})<BoxProps & { whatever: string }>(({ theme, someComponentProp }) => ({
    // styles    
}));

现在,我想将返回styles对象的函数分离到一个单独的主题文件中,这样我就可以将SomeContainer.tsxSomeContainer.styles.ts文件配对

问题-定义类型

样式文件的内容将导出样式:

export const SomeComponentStyles: ??? = ({ theme, someComponentProp }) => ({
    // styles    
});

MUI的Typescript类型定义非常复杂,我似乎无法正确定义导出的类型(???部分)。当然,您总是可以将配置设置得不那么严格,这样您就可以使用any或甚至不提供类型,但这对vscode intellisense改善开发人员编写这些样式的体验没有帮助。

**问题不在于代码无法编译,而在于实际定义此类型,以便在为各种不同组件编写这些文件时获得更好的开发环境(即vscode)体验?**IDE将为函数参数对象以及生成的对象中的样式本身提供正确的代码提示。

我所尝试的
styled本身是一个function,它返回一个function,该function接受任意数量的参数(带有样式定义的对象或返回此类对象的函数-我的例子)。
看起来这应该是正确的类型,但它不是。

type StyleDefinition = Parameters<ReturnType<typeof styled>>[0];

还有一个额外的问题,我在泛型函数定义中提供了<BoxProps & { whatever: string }>类型,这意味着我的额外属性应该传播到函数参数。
这里有一个Sandbox可供参考。

brc7rcf0

brc7rcf01#

您实际上几乎已经做到了,但罪魁祸首是styled(Component, options)<AdditionalProps>()的函数重载,这使得操作它的类型变得笨拙。
TL; DR:

// Use argument at index 1 (instead of 0)
Parameters<ReturnType<typeof styled>>[1];

当直接执行styled(Component)()函数时,TypeScript能够推断要使用的正确函数重载。
在本例中,您使用的是2/ 4:

<AdditionalProps extends {}>(
  ...styles: Array<
    Interpolation<ComponentProps & SpecificComponentProps & AdditionalProps & { theme: T }>
  >
): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps, JSXProps>;

但是当试图单独定义参数,试图用TypeScript提取其类型Parameters内置实用程序类型时,TS只使用 * 最后一个函数重载 *,如Typescript pick only specific method from overload (to be passed to Parameters)中所述
因此,我们得到过载4/4:

<AdditionalProps extends {}>(
  template: TemplateStringsArray,
  ...styles: Array<
    Interpolation<ComponentProps & SpecificComponentProps & AdditionalProps & { theme: T }>
  >
): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps, JSXProps>;

因此,当尝试获取第一个参数(索引为0,直接执行函数时使用的参数)时,我们得到的是TemplateStringsArray类型,而不是调用函数时推断出的类型。
幸运的是,我们看到@mui团队提供了一个合理的API,其中的rest参数实际上与前面一个重载中的参数相同:因此,我们可以简单地得到下一个,而不是第一个(即,跳过索引0):

// Use argument at index 1 (instead of 0)
Parameters<ReturnType<typeof styled>>[1];

还有一个额外的问题,我在泛型函数定义中提供了<BoxProps & { whatever: string }>类型,这意味着我的额外属性应该传播到函数参数。
我们可以简单地用它的实际Component和AdditionalProps来定义函数,然后使用它的类型:

const createStyledBox = styled(Box, {
    name: "SomeContainer",
    slot: "Root"
})<BoxProps & { whatever: string }>;

type BoxStyles = Parameters<typeof createStyledBox>[1];

const boxStyles: BoxStyles = ({ theme, whatever }) => ({
  // styles
  padding: theme.spacing(1.5),
  //       ^? Theme
  backgroundColor: whatever === "val" ? "white" : "black",
  //               ^? string
});

const SomeContainer2 = createStyledBox(boxStyles);

Playground链接
如果您想进一步将样式从样式化组件创建中分离出来,您确实可以定义一个通用类型(可能带有泛型),并让TypeScript在您将其与creator函数一起使用时检查结构兼容性:

import { Interpolation, styled, Theme } from "@mui/system";

type InterpolationWithTheme<Props extends {}> = Interpolation<Props & { theme: Theme }>

const whateverStyles: InterpolationWithTheme<{ whatever: string }> = ({ theme, whatever }) => ({
    // styles
    padding: `${theme.spacing(1.5)} ${theme.spacing(2)}`,
    //          ^? Theme
    backgroundColor: whatever === "val" ? "#fdd" : "white",
    //               ^? string
});

const SomeContainer3 = styled(Box, {
    name: "SomeContainer",
    slot: "Root"
})<BoxProps & { whatever: string }>(whateverStyles); // Okay

Playground链接

相关问题