0

我正在尝试使用材质 UI 动态生成我的样式,makeStyles()但是当我Object.fromEntries结合使用时,无法正确推断出类型。

import * as React from "react";
import { makeStyles } from "@material-ui/core/styles";

const paperViewportSizes = {
  sm: '30',
  md: '50',
  lg: '66',
  xl: '80',
} 

const useStyles = makeStyles((theme) => ({
  App: {
    color: "hotpink"
  },
  ...Object.fromEntries(
    ['Sm', 'Md', 'Lg'].map((size, i) => [
      `contentPadding${size}`,
      { padding: theme.spacing((i + 1) * 2, (i + 1) * 2 + 1) },
    ]),
  ),
  ...Object.entries(paperViewportSizes).reduce(
    (acc, [key, size]) => ({
      ...acc,
      ...{
        [`paper_${key}_h`]: { height: `${size}vh` },
        [`paper_${key}_w`]: { width: `${size}vw` },
      },
    }),
    {
      paper_auto_h: { height: 'auto' },
      paper_auto_w: { width: 'auto' },
    }
  )
}));
export default function App() {
  const classes = useStyles(); // Record<"App" | "paper_auto_h" | "paper_auto_w", string>
  return (
    <>
    <div className={classes.App}>
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
    </>
  );
}

有什么我想念的吗?

在此处输入图像描述

4

1 回答 1

1

问题是它Object.fromEntries几乎无法推断出任何东西。如果你查看它的类型声明,你会看到:

interface ObjectConstructor {
    fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { 
        [k: string]: T 
    };
    fromEntries(entries: Iterable<readonly any[]>): any;
}

你可以看到,它要么返回any,要么{ [k: string]: T }。在前一种情况下,没有推论,在后一种情况下 - 对键类型没有限制。考虑这个例子:

const foo = Object.fromEntries<number>([
  // I'm writing "as const" because typescript expects to get
  // type "[string | number | symbol, number]", but if you don't
  // add "as const", it will consider ['a', 1] to be "(string | number)[]"
  ['a', 1] as const,
  ['b', 2] as const,
])

这里foo是 type { [k: string]: number },而不是type { [k in 'a' | 'b']: number },尽管这种类型会更精确。现在看看展开运算符会发生什么:

const bar = {
   ...foo,
   c: 8
}

这里bar是类型{ c: number }。你可以看到 typescript 只是被忽略了foo。我不是 TS 中传播算子推理工作方式的专家,如果您有兴趣,可以 google 一下。

无论如何,这就是您的代码中发生的事情,获得​​正确类型的唯一方法是手动转换类型

...(
  Object.fromEntries(/*...*/) as Record<
    'contentPaddingSm' | 'contentPaddingLg' | 'contentPaddingMd',
    { padding: string }
  >
)
于 2020-10-12T09:00:09.950 回答