Для этого случая использования я бы порекомендовал вспомогательную функцию, которая не изменяет предполагаемый тип componentStyles
, но который позволит вам только создавать объекты с правильными типами свойств:
const asComponentStyles = <T extends Record<keyof T, Style>>(t: T) => t;
Этоявляется универсальной функцией, где параметр типа T
имеет значение с ограничением , которое можно назначить на Record<keyof T, Style>
. Это само-ссылочное ограничение (известное как F-ограниченный полиморфизм ) и позволяет компилятору проверять, что тип T
, определенный из вызова функции, имеет для любого ключа в keyof T
свойства, назначаемыедо Style
. Давайте посмотрим на это в действии:
const componentStyles = asComponentStyles({
button: { color: 'red' },
heading: { fontSize: 18, lineHeight: 28 },
body: { fontSize: 12, lineHeight: 18 },
});
type ComponentName = keyof (typeof componentStyles);
// type ComponentName = "button" | "heading" | "body"
Это работает, как вы и планировали. И давайте удостоверимся, что он делает свою работу по предотвращению плохих Style
свойств:
const errorChecking = asComponentStyles({
okay: { color: 'chartreuse', lineHeight: 123 },
badColor: { color: 123, lineHeight: 123 }, // error!
// ~~~~~ <── 'number' is not assignable to type 'string | undefined'.
excessPropChecking: { colour: "grey" } // error!
// ┌──────────────> ~~~~~~~~~~~~~~
// Object literal may only specify known properties,
// but 'colour' does not exist in type 'Style'.
// Did you mean to write 'color'?
})
Также хорошо выглядит.
Обратите внимание, что вы можете избежать неприятностей сболее простая, не относящаяся к самому себе вспомогательная функция, использующая сигнатуру индекса, аналогичную той, которую вы делаете в своем вопросе:
const asComponentStylesIndex = <T extends { [k: string]: Style }>(t: T) => t;
const componentStylesIndex = asComponentStylesIndex({
button: { color: 'red' },
heading: { fontSize: 18, lineHeight: 28 },
body: { fontSize: 12, lineHeight: 18 },
}); // okay
Это работает, потому что TypeScript возьмет литерал объекта и даст ему неявное подпись индекса .
Но я бы не рекомендовал это, если вы собираетесь использовать значения интерфейса, поскольку в настоящее время им не разрешено получать неявные подписи индекса , в то время как F-ограниченная версия работает для интерфейсова также литералы объектов:
interface MyComponent {
foo: Style,
bar: Style
}
declare const myComponent: MyComponent; // value of an interface type
const works = asComponentStyles(myComponent); // okay
const doesntWork = asComponentStylesIndex(myComponent); // error!
// ┌───────────────────────────────────> ~~~~~~~~~~~
// Index signature is missing in type 'MyComponent'.
Не уверен, являются ли вещи с интерфейсом частью вашего варианта использования или нет.
В любом случае, надеюсь, это поможет;удачи!
Ссылка на код