是否可以在TypeScript中推断Record的键?

问题描述 投票:4回答:2

我正在使用JSS并且想要使用强类型键和值来定义style对象,而不是两次定义键。

第一次尝试:

const style: Record<string, CSSProperties> = {
  root: {
    background: 'red'
  },
  title: {
    fontWeight: 'bold'
  },
  ...
}

现在style没有强类型,因此编译器在访问style.nonExistingKey时不会发出警告。

第二次尝试:

如果我明确指定键如下:

const style: Record<'root' | 'title' | ... , CSSProperties> = {
  root: {
    background: 'red'
  },
  ...
}

然后我得到一个强类型的记录,即style.nonExistingKey将抛出一个错误。但是,此方法需要复制记录键,因为它们必须显式添加为通用参数。

第三次尝试:

我可以使用以下代码在事后创建强类型记录:

const styleObj = {
  root: {
    background: 'red'
  },
  title: {
    fontWeight: 'bold'
  },
  ...
}

const style = styleObj as Record<keyof typeof styleObj, CSSProperties>

但是,我丢失了记录的CSSProperties值的类型检查,所以这不是一个好的解决方案。

有没有办法做这样的事情:

const style: Record<T, CssProperties> = {
  root: {
    background: 'red'
  },
  ...
}

并将T自动推断为'root' | 'title' | ...等?

typescript
2个回答
3
投票

定义对象时可以使用辅助函数。该函数将具有一个类型参数,该参数要求所有proepries必须使用索引签名为CSSProperies类型。由于函数是通用的,结果将被正确输入(它实际上不会有索引签名,而只是定义的proepries)

function createStyleMap<T extends { [name: string]: CSSProperties }>(cfg: T)  {
  return cfg;
}
const style = createStyleMap({
  root: {
  background: 'red'
  },
  title: {
    fontWeight: 'bold'
  }
});

style.root //ok
style['hu'] // error

你也可以输入returnRecord,但我认为这不会增加任何价值

function createStyleMap<T extends { [name: string]: CSSProperties }>(cfg: T) : Record<keyof T, CSSProperties> {
  return cfg;
}

1
投票

Update

在学习了更多TypeScript之后,我设法让它在一个函数中运行。所以这里有新的,简短的工作解决方案。

助手功能:

import { StyleRulesCallback, Theme } from 'material-ui/styles';
import { CSSProperties } from 'react';

export const createStyleMap =
    <T extends keyof any>(callback: (theme: Theme) => Record<T, CSSProperties>):
    StyleRulesCallback<T extends string ? T : never> => callback;

组件示例:

import { withStyles, WithStyles } from 'material-ui/styles';
import React from 'react';
import { createStyleMap } from '../utils/utils';

interface OwnProps {
    dummyName: string;
}

const styles = createStyleMap(theme =>
    ({
        content: {
            height: '100%',
            padding: theme.spacing.unit * 3,
            overflowY: 'auto',
            boxSizing: 'border-box'
        },
        tableHead: {
            height: 56
        }
    })
);

type Props = OwnProps 
    & WithStyles<keyof ReturnType<typeof styles>>;

class DummyComponent extends React.Component<Props> {
    render() {
        return <div className={this.props.classes.content}/>;
    }
}

export default withStyles(styles)(DummyComponent);
© www.soinside.com 2019 - 2024. All rights reserved.