- 記事一覧 >
- ブログ記事

JS→ts-migrateでTypeScript化→ESLint導入 エラー対処メモ
■はじめに
だいぶ前の話ですが、このブログを最初 JS で作成していて、途中から airbnb / ts-migrate を使って、TypeScript にしました。
ts-migrate の直後と ESLint 導入の直後は、エラー多数でボロボロでした。
その時の対処内容を備忘録的に書こうと思います。
(備忘録と言うからには、本当はその時書かないといけない話ですが、巻き戻して、当時のことも思い出して書きました。)
あくまで一例で、設定で抑えられるとか、他の修正方法とかの細かい話しは省略しています。
エラーは、英語表記で出力されるように設定しました。(日本語で出力すると、ググっても情報が少ないため。)
別の箇所にも発生した同じエラーや、ここに書いてあること以外にもエラーがあったり、修正することにより別のエラーになることもありましたが、省略しています。
■ts-migrate その1
とりあえず、pages 配下の .js に対して、ts-migrate (JS から TypeScript へ変換) してみました。
$ cd itc-blog
$ npm install --save-dev typescript @types/react @types/node
$ npm install --save-dev ts-migrate
$ find pages/ -type f
pages/index.js
pages/_app.js
pages/_document.js
pages/list/[[...slug]].js
pages/blogs/[id].js
$ npx ts-migrate-full pages・・・
pages/_document.ts:45:1 - error TS1128: Declaration or statement expected.
45 }
~
Found 25 errors in the same file, starting at: pages/_document.ts:10
$ find pages/ -type f
pages/list/[[...slug]].tsx
pages/blogs/[id].tsx
pages/tsconfig.json
pages/_app.tsx
pages/_document.ts
pages/index.tsエラーが出まくりましたが、.js が.tsx と.ts に変わりました。
pages がソースコードのトップだと思って、相対パスで import していたところが全て
// @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module '../.... Remove this comment to see the full error message
のエラーになり、話になりません。
仕切り直しました。
Cannot use JSX...
エラー内容Cannot use JSX unless the '--jsx' flag is provided. ts(17004)
原因
JSX 部分をどうするのかオプションで指定されていない。または、設定されていない。
修正方法tsconfig.jsonに"jsx": "preserve" を追加する。
修正例
// "jsx": "preserve", /* Specify what JSX code is generated. */"jsx": "preserve", /* Specify what JSX code is generated. */■ts-migrate その2
一旦元に戻して、以下のようにソースコードを src ディレクトリに入れて、一つにまとめてから仕切り直しました。
$ cd itc-blog
$ mkdir src
$ mv libs lib hooks components pages src
$ npm install --save-dev typescript @types/react @types/node
$ npm install --save-dev ts-migrate
$ npx ts-migrate init srcここで、とりあえず、src/tsconfig.json
が作成されて、何も行われません。src/tsconfig.json を書き換えます。
$ vi src/tsconfig.json// "jsx": "preserve", /* Specify what JSX code is generated. */
↓
"jsx": "preserve", /* Specify what JSX code is generated. */$ npx ts-migrate rename src
$ mv components/post/tweet.ts components/post/tweet.tsx
$ mv components/Jadate.ts components/Jadate.tsx
$ mv pages/_document.ts pages/_document.tsxnpx ts-migrate rename src で .js -> .ts or .tsx にリネームされます。
画面(JSX)が関係すると、.tsx になりますが、そうならなかったファイルは、手動でリネームしました。
ソースコードを JS→TypeScript に書き換えます。
$ npx ts-migrate migrate srcエラーが検出された箇所は、 // @ts-expect-error ... とコメントが追加されますので、この部分を手動で対処していきます。
Unused...
エラー内容Unused '@ts-expect-error' directive. ts(2578)
原因
特に問題が無いのに、// @ts-expect-error が書かれている。
修正方法
// @ts-expect-errorを書いたのは、ts-migrate。今回の場合、srcに一旦ソースコードを移動して ts-migrate したため、依存関係がおかしくなり、同様のエラーが多発した。
// @ts-expect-error... を削除。
Could not find a declaration...
エラー内容Could not find a declaration file for module 'react-syntax-highlighter'. '/opt/dev/itc-blog/node_modules/react-syntax-highlighter/dist/cjs/index.js' implicitly has an 'any' type.Try `npm i --save-dev @types/react-syntax-highlighter` if it exists or add a new declaration (.d.ts) file containing `declare module 'react-syntax-highlighter';` ts(7016)
原因
型定義(.d.ts)が見つからない。
修正方法
型定義をインストール。(エラー文言に書いてある通り実行)
$ npm i --save-dev @types/react-syntax-highlighterProperty 'language'...
エラー内容Property 'language' does not exist on type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>'. ts(2339)
原因
プロパティ(引数)の定義が無い。
修正方法
プロパティを定義して、FunctionComponent に書き換え。
修正例
export default class Code extends React.PureComponent {
render() {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'language' does not exist on type 'Readon... Remove this comment to see the full error message
const { language, value } = this.props;
return (
<SyntaxHighlighter
language={(language === 'js' ? 'javascript' : language) || 'javascript'}
style={okaidia}
>
{value}
</SyntaxHighlighter>
);
}
}interface Props {
language: string;
value: string;
}
const Code: FunctionComponent<Props> = ({ language, value }) => {
return (
<SyntaxHighlighter
language={(language === 'js' ? 'javascript' : language) || 'javascript'}
style={okaidia}
>
{value}
</SyntaxHighlighter>
);
};
export default Code;Cannot find module...
エラー内容Cannot find module '../settings.yml' or its corresponding type declarations. ts(2307)
原因.ymlの型定義が無い。
修正方法
$ npx yaml-dts-gen settings.ymlで型定義(settings.d.ts)生成。import '../settings.d.ts'
追加。
Object literal may...
エラー内容Argument of type '{ headingsOffset: number; scrollSmoothOffset: number; tocSelector: string; contentSelector: string; headingSelector: string; hasInnerContainers: true; scrollSmooth: true; orderedList: false; }' is not assignable to parameter of type 'IStaticOptions'. Object literal may only specify known properties, and 'scrollSmoothOffset' does not exist in type 'IStaticOptions'. ts(2345)
原因node_modules/tocbot/index.d.ts に scrollSmoothOffset?: number; が定義されていない。
修正方法node_modules/tocbot/index.d.ts に scrollSmoothOffset?: number; を定義。
実際行ったのは、
単純に古い tocbot がインストールされたため、最新になるようにインストールしなおし。
$ rm -r package-lock.json node_modules
$ npm installis not assignable...
エラー内容Argument of type '(argument: string, options?: { additionalDigits?: 0 | 1 | 2 | undefined; } | undefined) => Date' is not assignable to parameter of type 'number | Date'. ts(2345)Jadate.tsx(6, 74): Did you mean to call this expression?
原因
引数がおかしい。
修正方法
number 型か Date 型を渡さないといけない。
修正例
return <time className="text-gray-600 text-center" dateTime={formatISO(parseISO: any(date))}>{format(parseISO: any(date), "yyyy年MM月dd日")}</time>;return (
<time className="text-gray-600 text-center" dateTime={formatISO(parseISO(date))}>{format(parseISO(date), "yyyy年MM月dd日")}</time>
);
//ts-migrateが良くない方向に変換していたので、元々の形に戻した。',' expected...
原因
文法的に良く分からない位置に : がある。(, と間違っていないか?と言っている。)
修正方法
文法見直し。
修正例
return <time className="text-gray-600 text-center" dateTime={formatISO(parseISO: any(date))}>{format(parseISO: any(date), "yyyy年MM月dd日")}</time>;return (
<time className="text-gray-600 text-center" dateTime={formatISO(parseISO(date))}>{format(parseISO(date), "yyyy年MM月dd日")}</time>
);
//ts-migrateが良くない方向に変換していたので、元々の形に戻した。'any' only refers...
エラー内容'any' only refers to a type, but is being used as a value here. ts(2693)
原因
文法的に良く分からない位置に any 型の宣言がある。
修正方法
文法見直し。
修正例
return <time className="text-gray-600 text-center" dateTime={formatISO(parseISO: any(date))}>{format(parseISO: any(date), "yyyy年MM月dd日")}</time>;return (
<time className="text-gray-600 text-center" dateTime={formatISO(parseISO(date))}>{format(parseISO(date), "yyyy年MM月dd日")}</time>
);
//ts-migrateが良くない方向に変換していたので、元々の形に戻した。Unexpected token...
エラー内容Unexpected token. Did you mean `{'}'}` or `}`? ts(1381)
原因}の位置がおかしい。...というか、文法的におかしくなっていて、芋づる式に出ると思われる。
修正方法
文法見直し。
修正例
return <time className="text-gray-600 text-center" dateTime={formatISO(parseISO: any(date))}>{format(parseISO: any(date), "yyyy年MM月dd日")}</time>;return (
<time className="text-gray-600 text-center" dateTime={formatISO(parseISO(date))}>{format(parseISO(date), "yyyy年MM月dd日")}</time>
);
//ts-migrateが良くない方向に変換していたので、元々の形に戻した。Property 'gtag'...
エラー内容Property 'gtag' does not exist on type 'Window & typeof globalThis'. ts(2339)
原因gtag が型定義されていない。
修正方法gtag を型定義する。(@types/gtag.js を npm install)
$ npm install --save-dev @types/gtag.jsExpected 1 arguments...
エラー内容Expected 1 arguments, but got 0. ts(2554)index.d.ts(387, 9): An argument for 'defaultValue' was not provided.
原因
引数が必要なのに、渡していない。
修正方法
空のオブジェクトを渡す。
修正例
export const TweetsMap = createContext();export const TweetsMap = createContext({});This expression...
エラー内容This expression is not callable.Type '{}' has no call signatures. ts(2349)
原因
関数として呼び出しできない。(該当関数は、実行時に定義される。)
修正方法
any 型にキャスト。
修正例
if (addTweet) {
addTweet(id);
return { ignore: true };
} if (addTweet) {
(addTweet as any)(id);
//any型にキャスト。エラーは出なくなったが、正直、良くない方法かも。
return { ignore: true };
}Property 'opera'...
エラー内容Property 'opera' does not exist on type 'Window & typeof globalThis'. ts(2339)
原因window に opera プロパティが定義されていない。
修正方法any型にキャスト。
修正例
})(navigator.userAgent || navigator.vendor || window.opera);})(navigator.userAgent || navigator.vendor || (window as any).opera);
//any型にキャスト。エラーは出なくなったが、正直、良くない方法かも。refers to a UMD global...
エラー内容'tocbot' refers to a UMD global, but the current file is a module. Consider adding an import instead. ts(2686)
原因
tocbot(サードパーティーモジュール)を使うのに、import していない。
修正方法
サードパーティーモジュールを import する。
修正例
import tocbot from 'tocbot';を追加
■ESLint 導入
$ npm install --save-dev ・・・ にて、いくつかパッケージを導入して、ESLint を導入しました。
"devDependencies": {
"@types/gtag.js": "0.0.4",
"@types/node": "^15.3.0",
"@types/react": "^17.0.6",
"@types/react-dom": "^17.0.5",
"@types/react-syntax-highlighter": "^13.5.0",
"@typescript-eslint/eslint-plugin": "^4.24.0",
"@typescript-eslint/parser": "^4.24.0",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react-hooks": "^4.2.0",
"patch-package": "^6.4.7",
"prettier": "^2.3.0",
"serve": "^11.3.2",
"stylelint": "^13.13.1",
"stylelint-config-recommended": "^5.0.0",
"typescript": "^4.2.4"
}以降、この設定を有効にした段階で表示されたエラーへの対処記録になります。
{
"env": {
"es6": true,
"node": true,
"browser": true,
"commonjs": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"settings": {
"react": {
"version": "detect"
}
},
"plugins": ["react-hooks", "react", "@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"prettier"
],
"rules": {
"react/prop-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": ["error"]
}
}
]
}Missing return type...
エラー内容Missing return type on function. eslint(@typescript-eslint/explicit-module-boundary-types)
原因
関数の return 値の型が指定されていない。
修正方法
関数の return 値の型を指定する。
修正例
const useMobileDevice = () => {const useMobileDevice = (): [boolean] => {
//: [boolean]追加export default function usePageView() {export default function usePageView(): void {
//何も返さない場合、: void追加Object pattern argument...
エラー内容Object pattern argument should be typed with a non-any type. eslint(@typescript-eslint/explicit-module-boundary-types)
原因
any 型が指定されている。
修正方法
any 型でごまかさずに、ちゃんと型を指定する。
修正例
import React from 'react';
import Head from 'next/head';
export const Header = ({
title
}: any) => {import React, { FC } from 'react';
import Head from 'next/head';
interface Props {
title: string;
}
export const Header: FC<Props> = ({ title }) => {
//any→PropsUnexpected any...
エラー内容Unexpected any. Specify a different type. eslint(@typescript-eslint/no-explicit-any)
原因
any 型が指定されている。
修正方法
ちゃんと型を指定する。
修正例
import React from 'react';
import Head from 'next/head';
export const Header = ({
title
}: any) => {import React, { FC } from 'react';
import Head from 'next/head';
interface Props {
title: string;
}
export const Header: FC<Props> = ({ title }) => {
//any→Propsis missing the..
エラー内容Type '{ key: any; id: any; }' is missing the following properties from type 'Tweet': br, caption ts(2739)
原因
必須のプロパティが無い。
修正方法
必須のプロパティを指定する。
修正例
static tweet関連の実装を変えて対処したため、実際にはこの直し方ではない。
<Tweet key={value.twitter_id} id={value.twitter_id} /><Tweet key={value.twitter_id} id={value.twitter_id} br={''} caption={''} />
//必須のプロパティ(Tweetコンポーネントの引数)を記述is defined but...
エラー内容'title' is defined but never used. eslint(@typescript-eslint/no-unused-vars)
原因
引数を取って、その引数を使っていない。
修正方法
使っていない引数指定を削除する。
修正例
export const Footer = ({
title
}: any) => {export const Footer: FC = () => {
//({title}: any)→()Using target...
エラー内容Using target="_blank" without rel="noreferrer" (which implies rel="noopener") is a security risk in older browsers: see https://mathiasbynens.github.io/rel-noopener/#recommendations eslint(react/jsx-no-target-blank)
原因target="_blank"を指定しているのに、rel="noreferrer"が指定されていない。(rel 属性に noreferrer を付けることで、参照先に対して参照元のリンクを渡さないようにする)
修正方法target="_blank"と一緒にrel="noreferrer"を指定する。
修正例
<a href={hatena_href} target="_blank" data-hatena-bookmark-title={hatena_title} data-tip="このページをはてなブックマークに追加する" onClick={handleClick}>
<svg className="hatena-svg w-7 h-7 text-gray-500" fill="#6B7280" x="0px" y="0px"
width="1024px" height="1024px" viewBox="0 0 1024 1024"> <a
href={hatena_href}
target="_blank"
data-hatena-bookmark-title={hatena_title}
data-tip="このページをはてなブックマークに追加する"
onClick={handleClick}
rel="noreferrer">
<svg
className="hatena-svg w-7 h-7 text-gray-500"
fill="#6B7280"
x="0px"
y="0px"
width="1024px"
height="1024px"
viewBox="0 0 1024 1024">
//rel="noreferrer"を追加。'React' must be...
エラー内容'React' must be in scope when using JSX eslint(react/react-in-jsx-scope)
原因
JSX を使っているのに React をインポートしていない。
修正方法import React from 'react';を追加する。
修正例
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { existsGaId, GA_ID } from '../lib/gtag'
export default class MyDocument extends Document {
render() {
return (
<Html lang="ja">import Document, { Html, Head, Main, NextScript } from 'next/document'
import { existsGaId, GA_ID } from '../lib/gtag'
import React from 'react';
//Reactのimport追加
export default class MyDocument extends Document {
render() {
return (
<Html lang="ja">
//エラー箇所の修正は無し。'React' must be...
エラー内容Require statement not part of import statement. eslint(typescript-eslint/no-var-requires)
原因
require ステートメントを使っている。
修正方法// eslint-disable-next-line @typescript-eslint/no-var-requires
で表示しないようにするか、requireの代わりにawait importを使う。
修正例
const path = require('path');// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');const path = require('path');const path = await import('path');
//require→await import
その他、宣伝、誹謗中傷等、当方が不適切と判断した書き込みは、理由の如何を問わず、投稿者に断りなく削除します。
書き込み内容について、一切の責任を負いません。
このコメント機能は、予告無く廃止する可能性があります。ご了承ください。
コメントの削除をご依頼の場合はTwitterのDM等でご連絡ください。









