tsc编译错误'error TS1008: Unexpected token; 'module, class, interface, enum, import or statement' expect

2019.7.20 修正了错误

昨天给遗留的JS代码写了类型声明,兴冲冲地跑去编译,结果报了一堆错,人都傻了:

H:\project\dataStore.d.ts(1,9): error TS1005: ';' expected.
H:\project\dataStore.d.ts(1,19): error TS1005: ';' expected.
H:\project\dataStore.d.ts(1,25): error TS1005: ';' expected.
H:\project\dataStore.d.ts(2,5): error TS1008: Unexpected token; 'statement' expected.
H:\project\dataStore.d.ts(19,1): error TS1008: Unexpected token; 'module, class, interface, enum, import or statement' expected.

当然了,不是直接编译声明文件,是在main里导入,然后编译main。这个声明文件是这样的:

declare namespace NSApp {
    class DataStore {
        private data: IData;

        constructor();

        add(key: string, val: any): void;

        get(key: string): any;

        getAll(): IData;

        remove(key: string): void;
    }

    interface IData {
        [email: string]: any;
    }
}

看起来是不是没什么问题?事实上,就是没问题。因为换了一个项目,同样的代码编译成功了。实在是神秘。

我一开始怀疑是tsc的调用出了问题。去全局库里查看tsc脚本的内容:

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\node_modules\typescript\bin\tsc" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\node_modules\typescript\bin\tsc" %*
)

可以看出,tsc在被调用的时候确实指向的就是全局库。如果不放心,还可以顺便看看Linux环境下的shell脚本确认一下,因为理论上库的表现应该是一致的;毕竟,按照信息隐藏的观点,操作系统是需要被隐藏的实现细节:

#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  "$basedir/node"  "$basedir/node_modules/typescript/bin/tsc" "$@"
  ret=$?
else 
  node  "$basedir/node_modules/typescript/bin/tsc" "$@"
  ret=$?
fi
exit $ret

而且,如果在npm的全局库里直接调用tsc,是可以编译的(同时也进一步说明了代码真的没问题)。那么,问题就不在npm上了。事实上,我在查TypeScript的issue的时候,看到了微软工程师的一句评论:在这里插入图片描述莫名感到一阵嘲讽……无地自容。然后在查看tsc的版本的时候,我注意到了一个细节:
在这里插入图片描述
然而事实上我安装的typescript是最新版的3.5.3。这是咋回事,版本变了?

然后我去查了一下这个版本。官网上最早的文档是1.1的……不过在1.5版本的文档里提到:

namespace keyword

TypeScript used the module keyword to define both “internal modules” and “external modules”; this has been a bit of confusion for developers new to TypeScript. “Internal modules” are closer to what most people would call a namespace; likewise, “external modules” in JS speak really just are modules now.

Note: Previous syntax defining internal modules are still supported.

Before:

module Math {
 export function add(x, y) { ... }
}

After:

namespace Math {
 export function add(x, y) { ... }
}
let and const support

ES6 let and const declarations are now supported when targeting ES3 and ES5.

事实上,直到1.5版本,才引入了namespace关键字和letconst关键字的支持,而这个1.0.3.0的版本显然是不支持的,所以编译不通过就解释得通了。

找到了编译不通过的原因,现在的问题是,为什么会有这个版本呢?查了一下,发现C盘里有这样一个文件夹:C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0。然后在系统变量的Path里也有这一条:
在这里插入图片描述
这就说得通了。我们都知道,对于Windows的环境变量来说,上面的会覆盖下面的,所以这个1.0.3.0版本覆盖了通过npm安装的3.5.3版本;而这个Microsoft SDKs文件夹是安装Visual Studio的时候(被)安装的。

以下是考据环节,不感兴趣的读者可以略过:

我当时装的是VS 2013,正好对应TS的1.0版本;因为TS1.0正式发行是在2014年4月3日前后(见参考资料,因为微软官网上关于这个的页面已经404了),正好对应当时的’Visual Studio 2013 and Visual Studio Web Express 2013 Spring Update’,从这个版本之后的VS里都有TS了。

而我装的是VS Community 2013 update 4,时间是2014年12月11日:
在这里插入图片描述
自然也是有的。应该就在Web Developer Tools里:
在这里插入图片描述
事实上,我换了另一台电脑,上面的TypeScript版本就不一样了,然后又换了一台没装过VS的电脑,里面就没有这个文件夹了,验证了这个猜想。也就是说,两年前为了学习C语言装的VS,在两年后坑了我……请允许我在这里用一个表情表达此刻我的心情:
在这里插入图片描述
找到了问题,那么就是解决方案了。目前我发现的方案有两种:

第一种,从源头上解决,删除这个文件夹(没影响的),或者从环境变量里删掉这一条。如果实在不放心,就调整一下环境变量的顺序,让npm的全局仓库在它上面,把它覆盖掉。

第二种,我们都知道Node会优先使用项目库,然后再使用全局库,所以我们可以把这个项目初始化成一个Node项目(如果之前不是Node项目),然后npm install typescript,安装一个项目内部的typescript依赖,这时候tsc就会优先寻找项目内部的typescript依赖,这样也能解决。

为什么跟Node有关?可以看看之前的tsc脚本,里面明确写了是通过Node调用的:

node  "%~dp0\node_modules\typescript\bin\tsc" %*

结束。

参考资料
  1. TypeScript 1.0 正式版发布!
  2. Visual Studio 2013 Update 4 Release Notes
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值