首页 > 解决方案 > 如何导入自定义 EventEmitter 并避免 TS Error: 2339?

问题描述

我目前正在使用普通 JavaScript 制作的样板之上使用 TypeScript 配置项目。我的问题是在 preLaunchTask 中的 TS 编译期间出现的。我无法访问我在本地导入的自定义 EventEmitter 的“报告”属性。以下是来自 TS 的以下错误:

error TS2339: Property 'report' does not exist on type 'EventEmitter'.

下面是testing.ts文件:

'use strict';

import cors from 'cors';
import fs from 'fs';
import runner from '../../test-runner';
import express from 'express';

export default function (app: express.Application) {

  app.route('/_api/server.js')
    .get(function (req: express.Request, res: express.Response, next: Function) {
      console.log('requested');
      fs.readFile(__dirname + '/server.js', function (err, data) {
        if (err) return next(err);
        res.send(data.toString());
      });
    });
  app.route('/_api/routes/api.js')
    .get(function (req: express.Request, res: express.Response, next: Function) {
      console.log('requested');
      fs.readFile(__dirname + '/routes/api.js', function (err, data) {
        if (err) return next(err);
        res.type('txt').send(data.toString());
      });
    });
  app.route('/_api/controllers/convertHandler.js')
    .get(function (req: express.Request, res: express.Response, next: Function) {
      console.log('requested');
      fs.readFile(__dirname + '/controllers/convertHandler.js', function (err, data) {
        if (err) return next(err);
        res.type('txt').send(data.toString());
      });
    });

  var error;
  app.get('/_api/get-tests', cors(), function (req: express.Request, res: express.Response, next) {
    console.log(error);
    if (!error && process.env.NODE_ENV === 'test') return next();
    res.json({ status: 'unavailable' });
  },
    function (req: express.Request, res: express.Response, next: Function) {
      if (!runner.report) return next();
      res.json(testFilter(runner.report, req.query.type, req.query.n));
    },
    function (req: express.Request, res: express.Response) {
      runner.on('done', function (report) {
        process.nextTick(() => res.json(testFilter(runner.report, req.query.type, req.query.n)));
      });
    });
  app.get('/_api/app-info', function (req: express.Request, res: express.Response) {
    var hs = Object.keys(res.header)
      .filter(h => !h.match(/^access-control-\w+/));
    var hObj = {};
    hs.forEach(h => { hObj[h] = res.header[h] });
    delete res.header['strict-transport-security'];
    res.json({ headers: hObj });
  });

};

function testFilter(tests, type, n) {
  var out;
  switch (type) {
    case 'unit':
      out = tests.filter(t => t.context.match('Unit Tests'));
      break;
    case 'functional':
      out = tests.filter(t => t.context.match('Functional Tests') && !t.title.match('#example'));
      break;
    default:
      out = tests;
  }
  if (n !== undefined) {
    return out[n] || out;
  }
  return out;
}

这是我尝试导入的测试运行器:

var analyser = require('./assertion-analyser');
var EventEmitter = require('events').EventEmitter;

var Mocha = require('mocha'),
    fs = require('fs'),
    path = require('path');

var mocha = new Mocha();
var testDir = './tests'


// Add each .js file to the mocha instance
fs.readdirSync(testDir).filter((file) => {
    // Only keep the .js files
    return file.substr(-3) === '.js';

}).forEach((file) => {
    mocha.addFile(
        path.join(testDir, file)
    );
});

var emitter = new EventEmitter();  
emitter.run = () => {

  let tests = [];
  var context = "";
  var separator = ' -> ';
  // Run the tests.
  try {
  var runner = mocha.ui('tdd').run()
    .on('test end', (test) => {
        // remove comments
        var body = test.body.replace(/\/\/.*\n|\/\*.*\*\//g, '');
        // collapse spaces
        body = body.replace(/\s+/g,' ');
        var obj = {
          title: test.title,
          context: context.slice(0, -separator.length),
          state: test.state,
          body: body,
          assertions: analyser(body)
        };
        tests.push(obj);
    })
    .on('end', () => {
        emitter.report = tests;
        emitter.emit('done', tests)
    })
    .on('suite', (s) => {
      context += (s.title + separator);

    })
    .on('suite end', (s) => {
      context = context.slice(0, -(s.title.length + separator.length))
    })
  } catch(e) {
    throw(e);
  }
};

module.exports = emitter;

还有我的tsconfig.json文件:

{
    "compilerOptions": {
        "target": "esnext",
        "moduleResolution": "node",
        "allowJs": true,
        "noEmit": true,
        "isolatedModules": true,
        "esModuleInterop": true,
        "noImplicitReturns": true,
        "lib": [
            "dom",
            "es6"
        ],
        "outDir": "public",
        "sourceMap": true
    },
    "include": [
        "src",
    ]
}

我试图尽我所能缩小我的问题范围,但我不知道为什么进口和出口似乎没有回应一个和另一个。最近我一直在解决很多重新配置错误,所以我的大脑可能会想太多。如果还有其他可用的代码,请随时告诉我。

标签: typescript

解决方案


我调整了我的“preLaunchTask”以完全匹配我的 TS 编译器脚本名称——这不是问题。我得到的 TypeScript 错误,即我的测试运行程序上的“报告”属性无效,是由于测试运行程序文件中的旧语法导致的,这是由于原始测试运行程序作者将属性直接分配给 EventEmitter 的实例。为了让 TS 编译器满意,我将测试运行程序代码调整为基于类的设置,将“报告”添加到构造函数中,从而避免来自原始测试运行程序的“void”或“() => void”返回值。

test-runner2.js

import analyser from '../../assertion-analyser';
import { EventEmitter } from 'events';

import Mocha from 'mocha';
import fs from 'fs';
import path from 'path';
import { connect } from 'http2';

const mocha = new Mocha();
const testDir = './tests';

// Add each .js file to the mocha instance.
fs.readdirSync(testDir).filter(file => {
    return file.substr(-3) === '.js';
}).forEach(file => {
    mocha.addFile(
        path.join(testDir, file)
    );
});

class TestEmitter extends EventEmitter {
    constructor() {
        this.report = []
    }

    run = () => {
        // Run the tests...
        let tests = [];
        let context = '';
        let separator = ' ~> ';

        try {
            const runner = mocha.ui('tdd').run()
                .on('test end', test => {
                    // remove comments
                    let body = test.body.replace(/\/\/.*\n|\/\*.*\\/g, '');
                    // collapse spaces
                    body = body.replace(/\s+/g, '');
                    const obj = {
                        title: test.title,
                        context: context.slice(0, -separator.length),
                        state: test.state,
                        body: body,
                        assertions: analyser(body)
                    };
                    tests.push(obj);
                })
                .on('end', () => {
                    emitter.report = tests;
                    emitter.emit('done', tests)
                })
                .on('suite', (s) => {
                  context += (s.title + separator);

                })
                .on('suite end', (s) => {
                  context = context.slice(0, -(s.title.length + separator.length))
                });
        } catch(err) {
            throw(err);
        }
    }
}

export default TestEmitter;

tsconfig.json:

{
    "compilerOptions": {
        "target": "es5",
        "moduleResolution": "node",
        "allowJs": true,
        "noEmit": true,
        "isolatedModules": true,
        "esModuleInterop": true,
        "noImplicitReturns": true,
        "lib": [
            "dom",
            "es6"
        ],
        "outDir": "public",
        "sourceMap": true
    },
    "include": [
        "src",
    ]
}

希望这可以帮助其他尝试将 TS 添加到香草 JS 样板的人。我也发布了我的配置文件。使用Node默认类classextendsEventEmitter


推荐阅读