在 Typescript、Webpack、React 项目中使用 css-in-js 时没有重载匹配此调用错误

Posted

技术标签:

【中文标题】在 Typescript、Webpack、React 项目中使用 css-in-js 时没有重载匹配此调用错误【英文标题】:No overload matches this call Error while using css-in-js in Typescript, Webpack, React Project 【发布时间】:2021-05-30 15:34:49 【问题描述】:

我有 Webpack、Typescript 和 React Hooks 项目,我在其中使用 CSS-in-js 将样式传递给 div。当我将鼠标悬停在 Menu 组件中的 style 道具上时,出现以下错误。我不确定应该在哪里绑定 CSSProperties。

(JSX attribute) React.htmlAttributes<HTMLDivElement>.style?: React.CSSProperties
Type ' display: string; margin: number; padding: number; alignItems: string; flexDirection: string; justifyContent: string; width: string; height: string; background: string; ' is not assignable to type 'CSSProperties'.
  Types of property 'flexDirection' are incompatible.
    Type 'string' is not assignable to type 'FlexDirection'.ts(2322)
index.d.ts(1768, 9): The expected type comes from property 'style' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'

菜单组件:

import React from 'react';

const Menu = () => (
    <div maxWidth="lg" style=styles.menuContainer>
        MENU COMPONENT
    </div>
)

export default Menu;

const styles = 
    menuContainer: 
        display: 'flex',
        margin: 0,
        padding: 0,
        alignItems: 'center',
        flexDirection: 'row',
        justifyContent: 'space-around',
        width: '100%',
        height: '5vh',
        background: 'rgba(212, 15, 24,0.8)',
    
;

tsconfig.json:


    "compilerOptions": 
        "outDir": "./public",
        "noImplicitAny": true,
        "module": "AMD",
        "target": "es6",
        "jsx": "react",
        "allowJs": true,
        "allowSyntheticDefaultImports": true,
        "moduleResolution": "node",
        "esModuleInterop": true
    ,
    "include": [ "src", "image.d.ts" ],
    "exclude": [ "node_modules", "**/*.spec.ts" ]

【问题讨论】:

如果我将 css-in-js 替换为内联样式 style=border: '1px solid red' ,TS 编译器不会抛出任何错误。 以下作品:- 导出常量样式:React.CSSProperties = display: "flex" ;我正在寻找的是在样式属性中创建不同样式的对象,例如outerContainer,innerContainer,它们不起作用。 【参考方案1】:

以下两者有区别:

<Component style= flexDirection: 'row'  />
// compared to
const style =  flexDirection: 'row' ;
<Component style= flexDirection: 'row'  />;

根据style 属性后面的类型,即React.CSSProperties,它预计flexDirection 的类型为FlexDirection,这是'row' | 'column' | ... 的联合类型。

在第一种情况下,TypeScript 足够聪明,可以意识到您正在尝试构造一个 React.CSSProperties,因此它会说“我们期望其中一个字符串值,而 'row' 就是其中之一,很好”。

在第二种情况下,当您预定义对象时,TypeScript 执行type widening 并且基本上认为flexDirection 只是一个字符串。 通常是这种情况,这就是为什么它是默认解释。 但是,当您将此值传递给 style 时,它现在会将您的对象的 flexDirection 视为 string 而不是 @ 987654334@常量。


多年来我提出的对开发人员最友好的解决方案(我和我的朋友有时都偶然发现了这个问题,尽管并不总是与 React 有关)是使用一种通用帮助函数:

// Using this as example, but you can directly use React.CSSProperties
// (or really any other type that benefits from this "pattern" below)
interface CSSProperties 
    flexDirection?: 'row' | 'column';


// This simply returns the object itself, but we benefit from the special typing.
// As for the cost of a single simple function call? Negligible and worth it.
function checkStylesheets<T extends  [key in keyof T]: CSSProperties >(obj: T): T 
    return obj;


// To showcase the original problem: TypeScript sees 'row' as just a string
const widenedStyle =  flexDirection: 'row' ;
// typeof widenedStyle =  flexDirection: string 

const styles = checkStylesheets(
    menuContainer: 
        flexDirection: 'row',
    ,
    unknownProperty: 
        // Type ' nonExistingField: number; ' is not assignable to type 'CSSProperties'.
        // Object literal may only specify known properties, and 'nonExistingField' does not exist in type 'CSSProperties'.ts(2322)
        nonExistingField: 123,
    ,
    invalidFlexDirection: 
        // Type '"invalid"' is not assignable to type '"row" | "column" | undefined'.ts(2322)
        flexDirection: 'invalid',
    ,
    notEvenAString: 
        // Type 'number' is not assignable to type '"row" | "column" | undefined'.ts(2322)
        flexDirection: 5,
    
);
// typeof styles = 
//     menuContainer: CSSProperties;
//     unknownProperty: CSSProperties;
//     invalidFlexDirection: CSSProperties;
//     notEvenAString: CSSProperties;
// 

const someStyle: CSSProperties = styles.menuContainer;
// Property 'nonExistingName' does not exist on type ' menuContainer: CSSProperties; unknownProperty: CSSProperties; invalidFlexDirection: CSSProperties; notEvenAString: CSSProperties; '.ts(2339)
const anotherStyle: CSSProperties = styles.nonExistingName;

使用自定义 CSSProperties 接口简化了示例

上述解决方案/示例列出了 cmets 中的一些结果样式,以及它产生的所有 TypeScript 错误。

:为什么需要辅助函数?为什么不简单地将styles 的类型设置或强制转换为Record&lt;string, CSSProperties&gt; 或类似的类型?

A:通过使用这个技巧,checkStylesheets 将验证传递的对象是否符合作为只有 CSSProperties 字段的对象的要求。但由于与keyof T 的通用性,它将具有相同键的对象,但作为值CSSProperties

这样做的主要好处是 styles 保留其密钥。这意味着 TypeScript 会警告我们,例如styles.nonExistingStyle,对于智能感知和发现错别字非常方便。特别是因为 style 属性接受 undefined!

:我们不能只用const styles = ... as const; 代替吗?

A:是的,这会起作用,但 TypeScript 不会验证您的样式,直到您将对象实际传递给 style 属性。此时,style 属性会报错,而使用上述解决方案,TypeScript 会在定义styles 时警告您,无论您是否使用它。 还有一个事实是styles 的类型将是一个非常复杂的对象,一旦您的对象中有多个(非微小)样式,智能感知(和 TypeScript 错误)将占据您的整个屏幕。与简单的 styleA: CSSProperties; styleB: CSSProperties; ... 截然不同。

:我们不能只做const styles: styleA: CSSProperties = styleA: ... ;吗?

A:是的,这同样有效,但它是双重工作。这是一个完全好的解决方案,现在它只取决于您喜欢哪种编码风格。如果您更喜欢查看例如const styles = MyStyles 而不是智能感知中的 const styles = styleA: CSSProperties; ...

【讨论】:

以上是关于在 Typescript、Webpack、React 项目中使用 css-in-js 时没有重载匹配此调用错误的主要内容,如果未能解决你的问题,请参考以下文章

从0开始的TypeScriptの五:webpack打包typescript

使用 npm 构建 webpack-typescript 库以用于其他 webpack-typescript 项目

webpack+typescript入门实例

是否可以在 TypeScript 导入中指定 webpack 加载器?

TypeScript + Webpack 环境搭建

webpack的学习感悟