LoginSignup
1
0

More than 3 years have passed since last update.

TypeScript + React + styled componentsで開発するのに行ったこと

Last updated at Posted at 2019-10-01

TypeScriptで値を書き換えられないようにする

TypeScriptの標準のReadonlyのジェネリック型だと、ネストしたデーター構造の場合にネストした階層に対しては書き換えられてしまう。

utility-typesDeepReadonlyを使うとネストした場合でも値を書き換えられないようにでできる。

Readonly

type Foo = Readonly<{
  first: {
    second: {
      name: string
    }
  }
}>

const foo: Foo = {
  first: {
    second: {
      name: 'name'
    }
  }
}

ネストしてない値を書き換えた場合

foo.first = { second: 'name2' } // TS2540: Cannot assign to 'first' because it is a read-only property.

ネストした値を書き換えた場合

foo.first.second.name = 'name2' // 値が書き換えられる

DeepReadonly

import { DeepReadonly } from 'utility-types'

type Foo = DeepReadonly<{
  first: {
    second: {
      name: string
    }
  }
}>

const foo: Foo = {
  first: {
    second: {
      name: 'name'
    }
  }
}

ネストしてない値を書き換えた場合

foo.first = { second: 'name2' } // TS2540: Cannot assign to 'first' because it is a read-only property.

ネストした値を書き換えた場合

foo.first.second.name = 'name2' // TS2540: Cannot assign to 'name' because it is a read-only property.

styled-componentsのthemeに型付けて定義してない色を付けた場合は型エラーにする

color.ts
export type Colors = {
  black: string
  white: string
}

type Color = keyof Colors

export const color = (color: Color): ((props: { theme: Theme }) => string) => props => props.theme.colors[color]
import React from 'react'
import styled, { ThemeProvider } from 'styled-components'
import { Colors, color } from './color'

type Theme = {
  colors: Colors
}

const theme: Theme = {
  colors: {
    black: '#000',
    white: '#fff'
  }
}

export const App: React.FC = () => (
  <ThemeProvider theme={theme}>
    <Main>main</Main>
  </ThemeProvider>
)

const Main = styled.div`
  color: ${color('black')};
`

定義されてない色を指定した場合

const Main = styled.div`
  color: ${color('green')}; // TS2345: Argument of type '"green"' is not assignable to parameter of type '"black" | "white"'.
`

透明度の指定

themeの色のパターンに同じ色でも違う透明度のものを定義してしまうとパターンが増えてしまうので、polishedrgbaという関数を使うと、themeの色に対して透明度の調整した色を指定できる。

import React from 'react'
import { rgba } from 'polished'
import styled from 'styled-components'
import { color } from './color'

const Main = styled.div`
  color: ${props => rgba(color('black')(props), 0.3)};
`

SVGをJSXとして扱う

jsxとして使う場合svgのタグのままでは使えなくて、 attributeに:-が入ってる場合は変換する必要がある。

stroke-width => strokeWidth

JSXに変換する方法

新しいsvgアイコンが増えない限り、毎回webpackのloaderで変換する必要がないためsvgrを使うことにした。

svgrの設定例

svgrrc.js
module.exports = {
  icon: true,
  prettierConfig: {
    semi: false,
    singleQuote: true,
    printWidth: 128
  },
  template(
    { template },
    opts,
    { imports, componentName, props, jsx, exports }
  ) {
    const typeScriptTpl = template.smart({ plugins: ['typescript'] })
    const name = `${componentName.name.replace('Svg', '')}: React.FC<SVGProps<SVGSVGElement>>`
    return typeScriptTpl.ast`
      import React, { SVGProps } from 'react';
      export const ${name} = props => ${jsx};
    `
  }
}
package.json
{
  "scripts": {
    "svgr:build": "svgr ./svgs --ext tsx --out-dir ./components/ui/icons"
  }
}

svgrで変換したコンポーネントの組込例

import React from 'react'
import styled from 'styled-components'
import { Icon } from './components/ui/icons/Icon'

const App: React.FC = () => (
  <StyledIcon />
)

const StyledIcon = styled(Icon)`
  width: 16px;
  height: 16px;
  fill: blue;
`

同じアイコンでサイズや色のバリエーションを変える例

const StyledIcon = styled(Icon)`
  width: 32px;
  height: 32px;
  fill: red;
`
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0