styled-components の theme、使っていますか?styled-components には、ThemeProvider
という、装飾などに関する規定値を提供する仕組みがあります。この様な構成になっているとします(通常 ThemeProvider は SPA ルーティングの上あたりに位置しますが、説明のため簡易な構成としています)
const Component: React.FC = () => (
<ThemeProvider theme={theme}>
<RedButton>RedButton</RedButton>
<GreenButton>GreenButton</GreenButton>
<BlueButton>BlueButton</BlueButton>
</ThemeProvider>
)
ThemeProvider に渡している theme
は次の様なものです。
export const theme = {
colors: {
red: '#f00',
green: '#0f0',
blue: '#00f'
},
layout: {
width: 960
}
} as const
theme は、styled-components コンストラクタ(styled(...))
にラップされた Component props に注入され、Template リテラルから参照するのが通常です。次の例は、Generics どころか型情報もありません。それにも関わらず、この参照props.theme.colors.blue
にはキッチリ型推論が効いています。いったいどこから提供されたものでしょうか?
const StyledComponent = styled(Component)`
background-color: ${props => props.theme.colors.blue}; /* (property) blue: "#00f" */
`
DefaultTheme 型を宣言結合拡張する
@types/styled-components
を確認しましょう(v4.4.2)。DefaultTheme
という型定義があります。これは先日の投稿と同じ仕組みを使った、型定義の注入テクニックです。DefaultTheme
として用意された空っぽの interface に対し、宣言結合拡張することで、プロジェクト固有の型定義を注入することが出来ます。
import 'styled-components'
// ______________________________________________________
//
export const theme = {
colors: {
red: '#f00',
green: '#0f0',
blue: '#00f'
},
layout: {
width: 960
}
} as const
// ______________________________________________________
//
type AppTheme = typeof theme
// ______________________________________________________
//
declare module 'styled-components' {
interface DefaultTheme extends AppTheme {}
}
今のところ私は遭遇したことがありませんが、ThemeProvider が複数ある場合は、この手法は使えません。ここで紹介した内容は以下のリポジトリで確認できます。