是否可以推断TypeScript中记录的键?

6ie5vjzr  于 2022-12-19  发布在  TypeScript
关注(0)|答案(4)|浏览(95)

我正在使用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'
  },
  ...
}

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

第三次尝试:

我可以使用下面的代码在事后创建一个强类型的Record:

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' | ...等?

x7rlezfr

x7rlezfr1#

在定义对象时,可以使用帮助器函数。该函数将具有类型参数,该参数要求所有属性必须是使用索引签名的CSSProperties类型。由于该函数是泛型的,因此将正确键入结果(它实际上没有索引签名,而只有定义的属性)

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

您也可以键入它来返回Record,但我不认为这会增加任何值

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

cld4siwp2#

更新

在学习了更多的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);
crcmnpdw

crcmnpdw3#

[x in keyof y]就能解决这个问题:

const styleObj = {
    root: {
      background: 'red'
    },
    title: {
      fontWeight: 'bold'
    },
  }
type StyleObj = {
    [P in keyof typeof styleObj]?: CSSProperties
}
const newStyleObj:StyleObj = {
    root:{
        ...
    }
}

删除?以使所有字段都是必填字段。

elcex8rz

elcex8rz4#

对于TypeScript 4.9,我认为这最终可以通过使用新的satisfies操作符来实现:

const style = {
  root: {
    background: 'red'
  },
  title: {
    fontWeight: 'bold'
  },
  invalid: {
    invalidProperty: '' // Type '{ invalidProperty: string; }' is not assignable to type 'CSSProperties'.
                        // Object literal may only specify known properties, and 'invalidProperty' does not exist in type 'CSSProperties'.(2322)
  }
} satisfies Record<string, CSSProperties>;

type keys = keyof typeof style;  // "root" | "title" | "invalid"

style.nonExistingKey; // Property 'nonExistingKey' does not exist on type '{ ... }'.(2339)

style.root.fontWeight; // Property 'fontWeight' does not exist on type '{ background: string; }'.(2339)

运动场
如上面的示例所示,这将保留对象中所有键和值的强类型,并防止添加不满足类型的属性。

相关问题