一、安装TS,完成HelloWorld
1.nodejs/npm安装
首先要安装npm,参考js nodejs npm之间的关系+npm安装备忘
为了版本切换方便,建议安装nvm,参考使用nvm解决gulp ReferenceError: primordials is not defined
2.使用npm安装TS
//使用国内镜像
npm config set registry https://registry.npmmirror.com
npm install -g typescript
安装完成后我们可以使用 tsc 命令来执行 TypeScript 的相关代码,以下是查看版本号:
E:\ts\HelloWorld>tsc -v
Version 4.5.5
3.新建greeter.ts
let v = "hello world";
console.log(v);
4.打开命令行使用tsc生成js并运行
tsc greeter.ts//生成greeter.js
node greeter.js//运行
二、配置VS Code
1.安装VS Code
下载地址:https://code.visualstudio.com/。
如果下载速度过慢,参考https://www.cnblogs.com/onceweb/articles/15536291.html,原本的下载链接:https://az764295.vo.msecnd.net/stable/f80445acd5a3dadef24aa209168452a3d97cc326/VSCodeSetup-x64-1.64.2.exe
,将前面的CDN节点替换掉:http://vscode.cdn.azure.cn/stable/f80445acd5a3dadef24aa209168452a3d97cc326/VSCodeSetup-x64-1.64.2.exe
,就可以按正常速度下载了.
2.运行报错
[Running] ts-node "e:\ts\HelloWorld\greeter.ts"
'ts-node' �����ڲ����ⲿ���Ҳ���ǿ����еij���
���������ļ���
报错信息中,出现了ts-node,那这个东西可以百度一下看看干啥的。
当我们用 Typesript 来写 Node.js 的代码,写完代码之后要用 tsc 作编译,之后再用 Node.js 来跑,这样比较麻烦,所以我们会用 ts-node 来直接跑 ts 代码,省去了编译阶段。
3.安装ts-node,注意这一步失败了,解决方案在后面
参考
使用ts-node直接运行ts脚本
如何在VS Code中使用ts-node调试TypeScript
手写一个 ts-node 来深入理解它的原理
这个参考上面的链接,是说不能全局安装,会报错。其实为了各项目的版本管理方便,建议还是本地安装。
注意,在安装之前,最好先使用npm init命令将package.json生成,否则会提示:npm WARN saveError ENOENT: no such file or directory, open 'E:\ts\learnTsconfig\package.json'
然后执行如下命令:
npm i ts-node -D
现在,执行ts-node greeter.ts
ts-node : 无法将“ts-node”项识别为 cmdlet、函数、脚本文件或
可运行程序的名称。请检查名称的拼写,如果包括路径
,请确保路径正确,然后再试一次。
所在位置 行:1 字符: 1
+ ts-node greeter.ts
+ ~~~~~~~
+ CategoryInfo : ObjectNotFound: (ts-node:String) [],
CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
这是怎么回事,去官网看一下:https://github.com/TypeStrong/ts-node,没有找到针对性的解决方案,改成全局安装ts-node还是报错:
E:\ts\HelloWorld>ts-node
d:\nvm\v8.11.2\node_modules\ts-node\dist\repl.js:178
catch { }
^
SyntaxError: Unexpected token {
at createScript (vm.js:80:10)
又参考以下链接,还是没解决掉。
搭建一套支持TS的Node运行环境
ts-node 的那些坑
4.安装特定版本的ts-node
然后怀疑是npm的版本或者ts-node的版本兼容问题,后来参考使用ts-node demo.ts命令报错,使用了ts-node的老版本,解决掉了。
使用 npm uni -g ts-node 卸载掉ts-node
再使用 npm i -g ts-node@8.5.4下载旧一点的版本
5.断点调试
参考VSCode使用ts-node 调试TypeScript代码
为了断点调试,我们需要在tsconfig.json中开启sourceMap,然后为ts-node注册一个vsc的debug任务,修改项目的launch.json文件
{
"name": "Current TS File",
"type": "node",
"request": "launch",
"args": [
"${workspaceRoot}/greeter.ts" // 入口文件
],
"runtimeArgs": [
"--nolazy",
"-r",
"ts-node/register"
],
"sourceMaps": true,
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
按F5就弹出了这个,可是把launch.json中的注释去掉,还是报错。
参考以下这个,并未解决:
https://stackoverflow.com/questions/70441188/debug-a-json-with-comments
参考如何在VS Code中使用ts-node调试TypeScript
launch.json :
{
"version": "0.2.0",
"configurations": [
{
"name": "Current TS File",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/ts-node/dist/bin.js",
"args": [
"${relativeFile}"
],
"cwd": "${workspaceRoot}",
"protocol": "inspector"
}
]
}
因为我们是全局安装的ts-node,所以要把program路径改成实际的安装位置。但是运行起来,发现没报错,但也没输出
d:\nodejs\node.exe d:\nodejs\node_modules\ts-node\dist\bin.js greeter.ts
那没办法,把ts-node安装到本地项目中,再试一下,发现报错了:
Error: Cannot find module 'typescript'
没办法,把typescript也安装到本地,重新 运行,发现没报错,也没输出……
参考https://gist.github.com/cecilemuller/2963155d0f249c1544289b78a1cdd695还是不行
最后,换了一台电脑,解决了,原因还不清楚,估计和版本有关……
6.终端无法运行npm命令
参考【VSCODE】解决VSCODE“因为在此系统上禁止运行脚本“报错
- 以管理员身份运行vscode
- 终端运行 get-ExecutionPolicy ==>Restricted,表示状态是禁止的
- 终端运行 set-Execu tionPolicy RemoteSigned
7.参考vscode生成快捷注释模板的方法
按下shift+ctrl+p,输入snippets,新建全局代码文件片段,自定义文件名称,然后回车,就进入到了文件:
{
"My comments": {
"scope": "javascript,typescript",
"prefix": "//.",
"body": [
"/**",
"* @description: ",
"* @author: ",
"* @date: $CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND",
"* @version: V1.0.0",
"*/"
],
"description": "my comments"
}
}
在代码中输入//,回车即可
8.条件断点
如图,在断点窗口点那个小铅笔,输入表达式即可:
三、@types 类型定义
参考
在 Typescript 2.0 中使用 @types 类型定义
types 和 @types 是什么?
基于 Typescript 开发的时候,很麻烦的一个问题就是类型定义。导致在编译的时候,经常会看到一连串的找不到类型的提示。解决的方式经过了许多的变化,从 DefinitelyTyped 到 typings。最后是 @types。在 Typescript 2.0 之后,推荐使用 @types 方式。
1.TSD(已过时,不推荐)
npm install tsd -g
tsd install jquery --save
2.typings(已过时,不推荐)
npm install typings --global
typings install react --save
3.@types 类型定义
在 Typescript 2.0 之后,TypeScript 将会默认的查看 ./node_modules/@types 文件夹,自动从这里来获取模块的类型定义,当然了,你需要独立安装这个类型定义。
比如,你希望 core.js 的类型定义,那么,你需要安装这个库的定义库。
npm install --save @types/core-js
与我们安装一个普通的库没有区别。当然了,常用的 jquery 也有。Microsoft 在 The Future of Declaration Files 介绍了 TypeScript 的这个新特性。
4.一个例子
这里我通过一个例子来说明一下什么是 @types,这样大家理解起来更深刻一点。
当我们用 npm 等包管理工具安装第三方包的时候,有些包并不是 TypeScript 编写的,自然也不会导出 TypeScript 声明文件。这种情况下,如果我们在 TypeScript 项目中引入了这种包,则会编译报错(没有设置 allowJS)。举个例子,当我们通过npm install jquery --save
安装 jquery 包并引用的时候,TypeScript 会报错。
allowJS 是 TypeScript 1.8 引进的一个编译项。
报错内容如下:
Could not find a declaration file for module ‘jquery’. Try
npm install @types/jquery
if it exists or add a new declaration (.d.ts) file containingdeclare module 'jquery';
这里的意思是 TypeScript 没有找到 jquery 这个包的定义,你可以通过npm install @types/jquery
安装相关声明,或者自己定义一份.d.ts 文件,并将 jquery 声明为 module。
全世界不是 TypeScript 编写的包多了去了。即使你的包是 TypeScript 编写的,如果你没有导出声明文件,也是没用的。(TypeScript 默认不会导出声明文件,只会编译输出 JavaScript 文件)。因此 TypeScript 必须对这种情况提供解决方案,而上面的两种方案(安装 @types 和 自己 declare module)就是 TypeScript 官方提出的, 你可以选择适合你的方案。我的推荐是尽量使用 @types 下的声明,实在没有,再使用第二种方法。
值得一提的是,并不是所有的包都可以通过这种方式解决的, 能解决的是 DefinitelyTyped 组织已经写好定义的包, 好消息是比较流行的包基本都有。 如果你想查一个包是否在 @type 下,可以访问 https://microsoft.github.io/TypeSearch/
那么 TypeScript 是怎么找定义的,什么情况会找不到定义而报类似上面举的例子的错误,这里简单介绍下原理。
5.包类型定义的查找
就好像 node 的包查找是先在当前文件夹找 node_modules,在它下找递归找,如果找不到则往上层目录继续找,直到顶部一样, TypeScript 类型查找也是类似的方式。
具体来说就是:
- TypeScript 编译器先在当前编译上下文找 jquery 的定义。
- 如果找不到,则会去 node_modules 中的@types (默认情况,目录可以修改,后面会提到)目录下去寻找对应包名的模块声明文件。
@types/*
模块声明文件由社区维护,通过发布到@types 空间下。 GitHub - DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.
6.变量类型定义的查找
和包查找类似,默认情况下变量类型定义的查找也会去 @types 下去寻找。只不过并不是直接去 @types 找,而是有一定的优先级, 这个过程类似原型链或者作用域链。
比如如下代码:
const user: User = { name: "lucifer" };
Typescript 则会先在本模块查找 User 的定义。如果找到,则直接返回。 如果找不到, 则会到全局作用域找,而这个全局默认就是指的就是 @types 下的所有类型定义。(注意目录页是可以配的)
也就是说 @types 下的定义都是全局的。当然你可以导入 @types 下导出的定义,使得它们的作用域变成你的模块内部。
7.typeRoots 与 types
前面说了 TypeScript 会默认引入node_modules下的所有@types声明,但是开发者也可以通过修改tsconfig.json的配置来修改默认的行为.
tsconfig.json 中有两个配置和类型引入有关。
typeRoots: 用来指定默认的类型声明文件查找路径,默认为node_modules/@types, 指定typeRoots后,TypeScript 编译器会从指定的路径去引入声明文件,而不是node_modules/@types, 比如以下配置会从typings路径下去搜索声明
{
"compilerOptions": {
"typeRoots": ["./typings"]
}
}
types: TypeScript 编译器会默认引入typeRoot下所有的声明文件,但是有时候我们并不希望全局引入所有定义,而是仅引入部分模块。这种情景下可以通过types指定模块名只引入我们想要的模块,比如以下只会引入 jquery 的声明文件
{
"compilerOptions": {
"types": ["jquery"]
}
}
比如:
{
"compilerOptions": {
"types" : ["node", "lodash", "express"]
}
}
这样将只会包含 ./node_modules/@types/node, ./node_modules/@types/lodash 和 ./node_modules/@types/express ,其它的则不会被包含进来。
如果配置为"types": []
则不会包含任何包。
8.总结
- typeRoots 是 tsconfig 中 compilerOptions 的一个配置项,typeRoots 下面的包会被 ts 编译器自动包含进来,typeRoots 默认指向 node_modules/@types。
- @types 是 scoped packages(感谢 Mickey 的指出 ),和@babel 类似。@types 下的所有包会默认被引入,你可以通过修改 compilerOptions 来修改默认策略。
- types 和 typeRoots 一样也是 compilerOptions 的配置,指定 types 后,typeRoots 下只有被指定的包才会被引入。
9.实践
首先,使用npm init生成默认的package.json,参考npm package.json scripts
然后npm i -D @types/node,在本地安装NodeJs的类型声明
四、自定义的d.ts
参考
TypeScript 中的 .d.ts 文件有什么作用
如何编写一个d.ts文件
TypeScript Handbook(中文版)
说白了就是定义了一些接口,使得你用typescript编程的时候调用此模块,IDE有提示。。。当然还会定义很多export的数据类型,和inferface 供外部模块调用。很显然就是数据规范。e.gtypescript:import * as mysql from 'mysql';
翻译成js 就是:require('mysql')
其实就是做了一层符合typescript的数据规范。
1.全局变量
declare var aaa:number
declare var aaa:number|string //注意这里用的是一个竖线表示"或"的意思
declare const max:200 //常量
2.全局函数
/** id是用户的id,可以是number或者string */
decalre function getName(id:number|string):string
有时候同一个函数有若干种写法:
get(1234)
get("zhangsan",18)
那么d.ts对应的写法:
declare function get(id: string | number): string
declare function get(name:string,age:number): string
3.类
declare class Person {
static maxAge: number //静态变量
static getMaxAge(): number //静态方法
constructor(name: string, age: number) //构造函数
getName(id: number): string
}
4.带属性的对象
全局变量myLib包含一个makeGreeting函数, 还有一个属性numberOfGreetings指示目前为止欢迎数量。
let result = myLib.makeGreeting("hello, world");
console.log("The computed greeting is:" + result);
let count = myLib.numberOfGreetings;
使用declare namespace描述用点表示法访问的类型或值。
declare namespace myLib {
function makeGreeting(s: string): string;
let numberOfGreetings: number;
}
5.prototype
prototype类型需要与class保持一致