使用道具有条件地设置样式组件中的属性以使用 TypeScript 对主题对象进行索引?
Conditionally set attributes in styled-component using props to index on theme object with TypeScript?
概述
我有一个 next.js
(react) TypeScript 项目正在使用 styled-components
ThemeProvider
。我正在尝试对 theme
对象进行索引,该对象将基于 rem
的字体大小存储为 strings
并按磅值 (integer
) 进行索引。我 运行 遇到的问题是我无法根据从组件传递的道具动态设置索引。错误消息、代码示例、尝试的解决方案和观察结果如下
错误信息
Element implicitly has an 'any' type because expression of type 'number' can't be used to index type '{ 14: string; 12: string; 10: string; }'.
No index signature with a parameter of type 'number' was found on type '{ 14: string; 12: string; 10: string; }'.
代码示例
主题对象类型定义
styled.d.ts
import "styled-components";
declare module "styled-components" {
export interface DefaultTheme {
fonts: {
size: {
14: string;
12: string;
10: string;
};
}
}
}
样式组件theme
对象
theme.ts
import { DefaultTheme } from "styled-components";
const theme: DefaultTheme = {
fonts: {
size: {
14: "0.875rem",
12: "0.75rem",
10: "0.625rem"
},
}
};
export { theme };
样式组件
components.ts
import styled from "styled-components";
const StyledThingie = styled('div')`
/*
HERE IS WHERE THIS ISSUE IS
I want to conditionally inject the prop (if fontSize is passed)
into the size index to get the rem-based size, and set a default
font-size if no prop is passed.
props.theme.fonts.size[10] works just fine.
*/
font-size: ${props => (props.fontSize ? props.theme.fonts.size[props.fontSize] : props.theme.fonts.size[10])};
`;
export {
StyledThingie
}
React 功能组件
Thingie.tsx
import React from "react";
import styled from "styled-components";
import { StyledThingie } from "./components.ts";
const Thingie = styled(StyledThingie)`
display: block;
`;
const ThingieComponent: React.FC<any> = (props) => {
return(
<Thingie fontSize={14}> // HERE IS WHERE I"M PASSING THE PROP
<p>This paragraph should be set to 0.875rem or theme.fonts.size[14] </p>
</Thingie>
);
}
尝试过的解决方案
我试图根据一些 TypeScript docs 定义一个 index signature
,但不清楚如何实现它。这是我的尝试:
interface FontSize {
[index: string]: number;
}
export type StyledThingieProps = {
fontSize?: FontSize;
}
const StyledThingie = styled('div')<StyledThingieProps>`
size: ${props => (props.fontSize ? props.theme.fonts.size[props.fontSize] : props.theme.fonts.size[10])};
`;
观察
我的脑袋疼... ;)
当您使用变量索引具有非动态属性的类型时会导致该错误。因此,要绕过它,您需要更改 DefaultTheme.fonts.size
的类型声明以定义动态属性。 (基本上,类型检查器无法知道传递给索引的变量 ([]
) 是否会与声明的键之一完全匹配。所以你必须说 "I'm ok with any numeric or string key"。)
如果您仍想声明已知密钥:
export interface DefaultTheme {
fonts: {
size: {
[key: number]: string;
14: string;
12: string;
10: string;
};
}
但是,如果您不关心具体的字体大小,您可以这样做
export interface DefaultTheme {
fonts: {
size: {
[key: number]: string;
};
}
这两个选项修复了类型定义以接受任何数字作为键。或者,您可以将 fontSize
强制转换为一个键类型(使用 keyof
),这基本上是一个 enum
,它将对象的所有键作为值。你告诉类型检查器 "this variable should be any of these specific values (any of the keys in the specific type)":
// in the template where you use the font size
props.theme.fonts.size[props.fontSize as keyof typeof props.theme.fonts.size]
// or using the type declaration directly
props.theme.fonts.size[props.fontSize as keyof DefaultTheme["font"]["size"]]
您通过声明字体大小的类型也走在了正确的轨道上,但是您的语法有点错误:
export type StyledThingieProps = {
fontSize?: keyof DefaultTheme["font"]["size"];
}
这是另一种修复方法。
我是这样解决的:
export const TextInput = styled.TextInput.attrs(({theme}) => ({ placeholderTextColor: theme.colors.primary, }))
font-family: ${({ theme }) => theme.fonts.interBold};
font-size: 18px;
color: ${({ theme }) => theme.colors.primary};
;
概述
我有一个 next.js
(react) TypeScript 项目正在使用 styled-components
ThemeProvider
。我正在尝试对 theme
对象进行索引,该对象将基于 rem
的字体大小存储为 strings
并按磅值 (integer
) 进行索引。我 运行 遇到的问题是我无法根据从组件传递的道具动态设置索引。错误消息、代码示例、尝试的解决方案和观察结果如下
错误信息
Element implicitly has an 'any' type because expression of type 'number' can't be used to index type '{ 14: string; 12: string; 10: string; }'.
No index signature with a parameter of type 'number' was found on type '{ 14: string; 12: string; 10: string; }'.
代码示例
主题对象类型定义
styled.d.ts
import "styled-components";
declare module "styled-components" {
export interface DefaultTheme {
fonts: {
size: {
14: string;
12: string;
10: string;
};
}
}
}
样式组件theme
对象
theme.ts
import { DefaultTheme } from "styled-components";
const theme: DefaultTheme = {
fonts: {
size: {
14: "0.875rem",
12: "0.75rem",
10: "0.625rem"
},
}
};
export { theme };
样式组件
components.ts
import styled from "styled-components";
const StyledThingie = styled('div')`
/*
HERE IS WHERE THIS ISSUE IS
I want to conditionally inject the prop (if fontSize is passed)
into the size index to get the rem-based size, and set a default
font-size if no prop is passed.
props.theme.fonts.size[10] works just fine.
*/
font-size: ${props => (props.fontSize ? props.theme.fonts.size[props.fontSize] : props.theme.fonts.size[10])};
`;
export {
StyledThingie
}
React 功能组件
Thingie.tsx
import React from "react";
import styled from "styled-components";
import { StyledThingie } from "./components.ts";
const Thingie = styled(StyledThingie)`
display: block;
`;
const ThingieComponent: React.FC<any> = (props) => {
return(
<Thingie fontSize={14}> // HERE IS WHERE I"M PASSING THE PROP
<p>This paragraph should be set to 0.875rem or theme.fonts.size[14] </p>
</Thingie>
);
}
尝试过的解决方案
我试图根据一些 TypeScript docs 定义一个 index signature
,但不清楚如何实现它。这是我的尝试:
interface FontSize {
[index: string]: number;
}
export type StyledThingieProps = {
fontSize?: FontSize;
}
const StyledThingie = styled('div')<StyledThingieProps>`
size: ${props => (props.fontSize ? props.theme.fonts.size[props.fontSize] : props.theme.fonts.size[10])};
`;
观察
我的脑袋疼... ;)
当您使用变量索引具有非动态属性的类型时会导致该错误。因此,要绕过它,您需要更改 DefaultTheme.fonts.size
的类型声明以定义动态属性。 (基本上,类型检查器无法知道传递给索引的变量 ([]
) 是否会与声明的键之一完全匹配。所以你必须说 "I'm ok with any numeric or string key"。)
如果您仍想声明已知密钥:
export interface DefaultTheme {
fonts: {
size: {
[key: number]: string;
14: string;
12: string;
10: string;
};
}
但是,如果您不关心具体的字体大小,您可以这样做
export interface DefaultTheme {
fonts: {
size: {
[key: number]: string;
};
}
这两个选项修复了类型定义以接受任何数字作为键。或者,您可以将 fontSize
强制转换为一个键类型(使用 keyof
),这基本上是一个 enum
,它将对象的所有键作为值。你告诉类型检查器 "this variable should be any of these specific values (any of the keys in the specific type)":
// in the template where you use the font size
props.theme.fonts.size[props.fontSize as keyof typeof props.theme.fonts.size]
// or using the type declaration directly
props.theme.fonts.size[props.fontSize as keyof DefaultTheme["font"]["size"]]
您通过声明字体大小的类型也走在了正确的轨道上,但是您的语法有点错误:
export type StyledThingieProps = {
fontSize?: keyof DefaultTheme["font"]["size"];
}
这是另一种修复方法。
我是这样解决的:
export const TextInput = styled.TextInput.attrs(({theme}) => ({ placeholderTextColor: theme.colors.primary, }))
font-family: ${({ theme }) => theme.fonts.interBold};
font-size: 18px;
color: ${({ theme }) => theme.colors.primary};
;