search
LoginSignup
11

More than 3 years have passed since last update.

posted at

updated at

TypescriptにSCSSをimportした上に型定義を使う

はじめに

Reactを使う時、css-loaderで:local(.className)または:local{...}と宣言した場合にclassNameがハッシュ化されexportされる。
それらのモジュールをimportする時、vscode(またはその他IDE)のIntelliSenseを使えると大変便利だと思う。
その方法をメモします。

やり方

フォルダ構成


+- demo-app/
   +- package.json
   +- tsconfig.json
   +- webpack.config.js
   +- html/
     +- index.html
   +- scripts/
     +- foo.tsx
   +- styles/
     +- foo.scss
     +- foo.scss.d.ts

css-loaderの導入

割愛します。
一応webpack.config.jsextensionsの領域に下記の拡張子を追加するのを忘れずに。

webpack.config.js
resolve: {
  extensions: [... ".sass", ".scss", ".css"]
}

スタイルの記入

styles/foo.scss
// ここはGlobal Style
* {
  color: royalblue;
}

// ここはScroped Style
:local {
  .bar {
    background-color: aquamarine;
  }
  .baz {
    font-size: 2em;
  }
}

スタイルの仮導入

.d.tsを作成しなくても、tsファイルの中で下記の記述のようにrequire()を使えば、別に導入できなくもないですが、TSスタイルではないので、ちゃんとimportできるようにする。

scripts/foo.tsx
const style = require('styles/foo')

型定義ファイル.d.tsの作成

TSにJSファイルを導入する時と同じように、ファイル名.d.tsを作成すればOK。
記述の仕方はこちらを参考していた。

styles/foo.scss.d.ts
declare module '*.scss' {
  const content: {
    bar: string;
    baz: string;
  };
  export default content;
}

TSにimport

scripts/foo.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'

import styles from 'styles/foo.scss' // 拡張子省略不可

const Hello: React.FC<{}> = props => (
  <div className={styles.bar}>Hello World!
  </div>
)

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello />,
    document.body.appendChild(document.createElement('div')),
  )
})

問題なければ、変数stylesを呼ぶ時、定義したclassはIntelliSenseに表示される。
hospital_infos_tsx_—_byouten-junior.png

結果

結果はこんな感じになる

ByoutenJunior.png
ByoutenJunior.png
ByoutenJunior.png

ここのclass名はコンパイルする度に変わる。

注意点

上記foo.scss.d.tsの書き方だと、下記の構文でのみ読み込める

import styles from 'styles/foo.scss'

以下のexport default contentを修正すれば、別の書き方でimportできる

declare module '*.scss' {
  const content: {
    bar: string;
    baz: string;
  };
- export default content;
+ export = content;
}
import * as styles from 'styles/foo.scss'
// または
import {foo, bar} from 'styles/foo.scss'

ちょっとスマートな使い方

前述のやり方だと、1つ.scssファイルに1つ.d.tsファイルを作らなきゃいけなくなるので、スタイルファイルが多い場合は大変見づらい。
styles/配下にindex.d.tsを作って、先ほどstyles/foo.scssの内容を移動すればいいと思う。

styles/index.d.ts
declare module 'styles/foo.scss' {
  const content: {
    bar: string;
    baz: string;
  };
  export = content;
}

declare module 'styles/bar.scss' {
  const content: {
    baz: string;
  };
  export = content;
}

こう記述すると、より綺麗にまとまると思う

余談

railsのwebpackerを使う時、デフォルトはjs/tsファイルのみwatchするが、下記の記述をconfig/application.rbに追加すれば、cssファイル(とscssファイル)もwatchしてくれる。

config/application.rb
Webpacker::Compiler.watched_paths += %w(PATH/TO/STYLES/**/*.css PATH/TO/STYLES/**/*.scss)

詳しい使い方はここを参照してください。

参考

https://qiita.com/nullabletypo/items/d3ac5f15f2405f9ac3fd
https://stackoverflow.com/questions/40382842/cant-import-css-scss-modules-typescript-says-cannot-find-module
https://qiita.com/terrierscript/items/56d2cc15f76df50dfee7

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
What you can do with signing up
11