- 記事一覧 >
- ブログ記事
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.tsx
npx 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-highlighter
Property '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 install
is 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.js
Expected 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→Props
Unexpected 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→Props
is 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等でご連絡ください。