そーす

I'm a programmer in Fukuoka. Please contact me saubre.app[at]gmail.com or Twitter DM.

TypeScriptでstyled-componentsに独自のPropsを渡したい

www.styled-components.com

最近はStyled-Component使ってます。

もともとstyleだけを分ける方法は使って無くて、

// Styleで分ける方法
const styles = {
  title: {
    fontSize: '2rem',
   color: '#333',
  },
};

export = ({ children }: Props) => (
  <main>
    <h1 style={styles.title}>{children}</h1>
  </main>
);
// コンポーネント毎に分ける方法
const Title = ({ text }: { text: string }) => (
  <h1 style={{ fontSize: '2rem', color: '#333'}}>
    {text}
  </h1>
);

export = ({ text }: Props) => (
  <main>
    <Title/>{text}</Title>
  </main>
);

こんな感じでコンポーネント毎に分ける方が好みでした。

コンポーネント名で判別できる方がわかりやすいですし、見た目もスッキリします。

しかしながらコンポーネントを分けていく上記の書き方だとコード数が増えるのが悩みでした。

styled-componentはそんな悩みを解決してくれたやつでした。

const Title = Styled.h1`
  font-size: 2rem;
  color: #333;
`;

export = ({ text }: Props) => (
  <main>
    <Title>{text}</Title>
  </main>
)

いい感じに私がやりたかったことを実現してくれています。

あとcss本来書き方(ハイフン使える)で記述できる点やhoverやメディアクエリも書くことができるのは大きいですね。

新たな悩み

styled-componentsにはstyleを定義しているテンプレートリテラルの中でPropsを受け取ることができます。

このお陰でPropsによってスタイルを変えることが容易になっています。

const Title = Styled.h1`
  font-size: 2rem;
  color: ${({ active }: any)=> active ? '#AAA' : '#333'};
`;

export = ({ text, active }: Props) => (
  <main>
    <Title active={active}>{text}</Title>
  </main>
)

しかし、TypeScritpだとTitleactiveが渡せません(エラーが出ます)。

理由はh1のPropsにactiveが定義されていないからです。

じゃあコンパイル通すにはどうするんだって話ですが、とりあえず

1.Propsで変化するStyleだけ別で書く

2.Object spreadで回避

3.コンポーネントを返す関数(前に私がやっていた方法)を定義する

かなーと思いました。

Propsで変化するStyleだけ別で書く

const Title = Styled.h1`
  font-size: 2rem;
`;

export = ({ text, active }: Props) => (
  <main>
    <Title style={{ color: ${active ? '#AAA' : '#333' }}}>{text}</Title>
  </main>
)

うーん、微妙。

そもそも1つのコンポーネントのスタイルを別々で定義するのはバグの温床っぽい感じがするので嫌ですね。

Object spreadで回避

const Title = Styled.h1`
  font-size: 2rem;
  color: ${({ active }: any)=> active ? '#AAA' : '#333'};
`;

export = ({ text, active }: Props) => (
  <main>
    <Title {...{active}}>{text}</Title>
  </main>
);

コンパイルは通りますが、TitleのPropsのタイプチェックはもちろん行われていません。

TypeScript使ってタイプチェック無視する書き方は出来る限り避けたいですね…

[追記] これコンパイル通りません。onClickなどの存在するPropsを追加するとコンパイルが通るようです。

コンポーネントを返す関数(前に私がやっていた方法)を定義する

const Title = ({ active, children }: { active: boolean, children: any }) => {
  const Cmp = Styled.h1`
    font-size: 2rem;
    color: ${({ active }: any)=> active ? '#AAA' : '#333'};
  `;
  return <Cmp>{children</Cmp>
}

export = ({ text, active }: Props) => (
  <main>
    <Title active={active}>{text}</Title>
  </main>
);

うーん、って感じです。

結論

悩んでます。