LoginSignup
104
47

More than 3 years have passed since last update.

any警察24時👮‍♂️

Last updated at Posted at 2019-12-25

これは TypeScript Advent Calendar 2019 22日目の代打記事です。

前回の投稿が好評だったため、ブラッシュアップ・CLIツール化し、any警察👮‍♂️を npm publish しました。READMEに記載したとおり、devDependencies に追加し、npx anycopを実行するとプロジェクトのany診断ができます。
anycop: https://www.npmjs.com/package/anycop

おことわり

これはanyを駆逐せよ、という目的で作ったものではありません。anyの使用状況が可視化されれば、課題点を把握し、チームで共有する事が出来るでしょう。時間のある時、型安全のカバレッジを向上する様な取り組みが出来れば、それはとても素晴らしい事です。any は悪者ではありません。TypeScript にこの仕様がなければ、今の JavaScript エコシステムに静的型付けを適用することは出来なかったでしょう。このツールで、any とより良い関係を築けたら幸いです。

変更点

前回投稿では「変数定義のany推論」に限定しましたが、パワーアップし、次の anyを検知する様になりました。

  • 変数宣言の any型推論
  • 関数引数の any型推論
  • BindingElementの any型推論
  • 関数の any戻り型推論
  • Arrow関数の any戻り型推論
  • AsExpression(as any アサーション)

TypeScriptプロジェクトにインストールし、実行した結果は以下の様になります。

image.png

変数宣言の any型推論

変数の明示的なany注釈はもちろん、any 推論も検知します。

function greet(message: any) {
  return message
}
// inferred "any" at VariableDeclaration
const message = greet('hello')

関数引数の any型推論

引数の明示的なany注釈はもちろん、参照している型定義が any 相当の場合も検知します。

// annotation of "any" at ParameterDeclaration (message)
function greet(message: any) {
  return message
}

BindingElementの any型推論

BindingElementで展開された参照が、any推論されている時も検知します。

type Props = { a: any, b: any }
// inferred "any" at BindingElement (a & b)
function greet({ a, b }: Props) {
  return { a, b }
}

関数の any戻り型推論

戻り型の明示的なany注釈はもちろん、any になってしまっている戻り型も検知します。

// inferred "any" at FunctionDeclReturn
function greet() {
  const message: any = 'hello'
  return message
}

Arrow関数の any戻り型推論

戻り型の明示的なany注釈はもちろん、any になってしまっている戻り型も検知します。

// inferred "any" at ArrowFunctionReturn
const abc = (param: string) => {
  switch (param) {
    case 'a':
      return true
    case 'b':
      return false
    case 'c':
      return '' as any
  }
}

AsExpression(as any アサーション)

asキーワードによるアサーションを検知します。

function greet() {
  return 'hello' as any // at AsExpression
}

anycop.config.js

プロジェクトルートにanycop.config.jsを設置することで、固有の設定ができます。errorThretholdはエラー報告の閾値です。「TypeSafe Coverage」の合計がこの数値を下回った場合、エラーを発生させることができます。インテグレーションツールへの導入などに活用してください。

module.exports = {
  targetDir: ".",
  errorThrethold: 0.2
}

検査項目増加による実装の変更点

ts.SourceFilets.Nodeのルートノードであり、そこを起点に実行する再帰関数(visit関数)を見直しました。ts.Nodets.SyntaxKindを持ち合わせており、この SyntaxKind で処理を選り分けています。

function visit(node: ts.Node) {
  switch (node.kind) {
    case ts.SyntaxKind.VariableDeclaration:
      // 変数宣言であれば
      // ....処理
      break
    case ts.SyntaxKind.Parameter:
      // 引数であれば
      // ....処理
      break
    case ts.SyntaxKind.BindingElement:
      // BindingElementであれば
      // ....処理
      break
    case ts.SyntaxKind.FunctionDeclaration:
      // 関数宣言であれば
      // ....処理
      break
    case ts.SyntaxKind.ArrowFunction:
      // アロー関数宣言であれば
      // ....処理
      break
    case ts.SyntaxKind.AsExpression:
      // アサーションであれば
      // ....処理
      break
  }
  ts.forEachChild(node, visit)
}
visit(source)

ts.TypeChecker.getReturnTypeOfSignature

「うっかり関数戻り型がanyに落ちていた…」という検査を追加しました。ts.SignatureDeclarationは、関数宣言やアロー関数を指します。ts.TypeCheckerに備わったgetSignatureFromDeclaration関数とgetReturnTypeOfSignature関数を利用することで、flags を得ることができます。これをもってts.TypeFlags.Anyと一致しているかを判定し、Anyか否かを判定しています。

// ts.ArrowFunction / ts.FunctionDeclaration は
// ts.SignatureDeclaration のサブタイプ
// node: ts.SignatureDeclaration
const signature = checker.getSignatureFromDeclaration(node)
if (signature) {
  const { flags } = checker.getReturnTypeOfSignature(signature)
  return checkAny(source, node, flags, name)
}
104
47
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
104
47