Typescript

typescript不检查node_moduls

月盾

tsconfig.json 中 exclude node_modules,但 tsc 还是报错。

node_modules/connect-mongo/src/types.d.ts:113:66 - error TS2694: Namespace 'global.Express' has no exported member 'SessionData'.

113         get: (sid: string, callback: (err: any, session: Express.SessionData | null) => void) => void;                                                                     ~~~~~~~~~~~

node_modules/connect-mongo/src/types.d.ts:114:45 - error TS2694: Namespace 'global.Express' has no exported member 'SessionData'.

114         set: (sid: string, session: Express.SessionData, callback?: (err: any) => void) => void;
                                                ~~~~~~~~~~~

node_modules/connect-mongo/src/types.d.ts:118:47 - error TS2694: Namespace 'global.Express' has no exported member 'SessionData'.

118         touch: (sid: string, session: Express.SessionData, callback?: (err: any) => void) => void;

另一种解决方案:

关于svelte框架——sapper和sveltekit的发展

月盾

虽然您可能现在还没有听说过svelte,但是其实svelte的发展速度超过了你的想象。

本文主要讲的是关于sapper和sveltekit这两款框架的发展。

svelte作者里奇·哈里斯(Rich Harris)在2020年10月的svelte峰会上表示:sapper永远不会发布1.0版本。

也就是说sapper不会发布正式版,一直处于非稳定版本。也可能放弃更新。

主要原因是sapper多年来代码库变得凌乱,但更主要的原因是最近网络发生了很大变化。

而作者放弃sapper后的另一种选择是开发SvelteKit

SapperSvelteKit都是svelte的开发框架,类似于vue的nuxt框架。

sveltekit包含的功能有:

  • 服务端渲染(SSR)

  • 路由

  • typescript支持

  • less, scss支持

  • serverless

  • vite打包

可以看到,sveltekit几乎包含了所有我们想要的功能,既能高效开发,又有高性能。

创建sveltekit的方法:


mkdir my-app

cd my-app

npm init svelte@next

npm install

npm run dev

需要注意,您的nodejs版本需要更新到v12以上,否则可能出现以下错误:


$ npm run dev -- --open


> sveltekit-app@0.0.1 dev D:\workspace\sveltekit-app

> svelte-kit dev "--open"

D:\workspace\sveltekit-app\node_modules\@sveltejs\kit\svelte-kit.js:2

import './dist/cli.js';

	SyntaxError: Unexpected string
	at Module._compile (internal/modules/cjs/loader.js:723:23)
	at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
	at Module.load (internal/modules/cjs/loader.js:653:32)
	at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
	at Function.Module._load (internal/modules/cjs/loader.js:585:3)
	at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
	at startup (internal/bootstrap/node.js:283:19)
	at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
									   
	npm ERR! code ELIFECYCLE
	npm ERR! errno 1
	npm ERR! sveltekit-app@0.0.1 dev: `svelte-kit dev "--open"`
	npm ERR! Exit status 1
	npm ERR!
	npm ERR! Failed at the sveltekit-app@0.0.1 dev script.
	npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
   

使用sveltekit开发一个服务端渲染(SSR)项目

typescript开发sequelize返回ModelInstance或null值无法获取属性值

月盾

sequelize nulll

从图片中的代码可以看出 let userRecord = await item.getUser();获取到的是Bluebird<UserInstance | null>类型,然后在下面获取对象属性的时候报错,错误信息是:

[ts] Object is possibly 'null'.
let userRecord: UserInstance | null

说对象可能是null,所以无法获取其中的属性。遇到这种情况请设置typescript的编译选项,tsconfig.json文件中的 "strictNullChecks": true, /* Enable strict null checks. */ 默认是true,即严格null检查,设置为falsse即可。

typescript泛型的使用方法

月盾

typescript(以下简称ts)中泛型如何使用?(以下代码为ts书写) 个人认为开发语言中一些高级特性如果在不太理解的情况下不使用也无妨,无非是代码写的多一点,烂一点。但是我想作为程序猿大家还是会有所追求的。就以泛型来说,不使用也能正常开发,只不过在个别情况下需要写几份看起来相同的代码。比如在不使用泛型的情况下要求函数参数为number类型,并且返回number。

function a(args: number ): number {
	return args;
}
console.log(a(123));

又有另外一个要求,参数为string类型,并且返回string

function b(args: string ): string {
	return args;
}
console.log(b("sdg"));

如果还有其他类似的要求,就要不停的写类似格式的代码,那么改进一下:

function c(args: any ): any {
	return args;
}
console.log(c("sdg"));

这样是可以接受任何类型参数并且返回,但缺点是可以知道能传入任何类型而不知道返回的具体类型是什么,只知道是any类型。将上面的c函数稍作修改:

function c(args: any ): any {
    let n = args + "变成了字符串";
    //甚至更多的处理
	return n;
}
console.log(c(1));

c函数参数是数字,返回的可能是其他类型。 知道前面几种写法的不足再对比一下泛型的写法:

function d<A>(args: A ): A {
    let n = args + "变成了字符串";
    //甚至更多的处理
	return n;
}
console.log(d(1));

FhO7R54TnMePUhwcnry3fo8AJz7_ d函数要求参数是A类型,并且返回A类型,但是实际返回的类型可能由number类型变成了string类型,所以提示错误。其中<>中可以是任意大小写的英文,中文也行,但数字不行,比如:

function d<Az>(args: Az ): Az {
    let n = args + "变成了字符串";
    //甚至更多的处理
	return n;
}
console.log(d(1));

<>中的值是指代某种基础类型或任何自定义类型,它只是一种形式。也可以有下面的形式:

function d<T, U>(args: T,ar: U): U {
	return ar;
}
console.log(d(2, "aaaa"))

其实就是提供一种形式供后面代码使用,没有提供就不能使用。

使用typescript开发nodejs的环境搭建(二)

月盾

完成了最基本的项目框架以后就是配置编辑器和编译选项,在没有特别配置的情况下,根目录下执行tsc会在ts后缀文件同级目录下生成js后缀的文件, 这样也没什么不可以,但是在编辑器列表中看着有点混乱,生成的js文件是不建议直接修改的,就算修改了下次修改ts文件编译后也会重置文件内容。 所以还是单独有个文件夹存放生成的js文件,这时就要配置tsconfig.json文件了。tsconfig.json文件可以通过tsc --init命令生成,自动生成内容比较简单,是可以直接使用的。 以下的配置是经过一些特别需求配置的,可以直接复制一下内容到tsconfig.json文件中,然后根据自己的需求加减内容。 其余参数可以参考:http://www.tslang.cn/docs/handbook/compiler-options.html

typescript编译配置

{
    "compilerOptions": {
        "module": "commonjs",//模块化规范
        "target": "es5",//生成js
        "noImplicitAny": true,//在表达式和声明上有隐含的'any'类型时报错
        "noImplicitReturns": true,//函数没有返回值提示
        "noFallthroughCasesInSwitch": true,//switch没有break提示
        "removeComments": true,//输出文件移除注释
        "noEmitOnError": true,//ts文件错误时不生成js
        "rootDir": "./",//需要编译的根目录
        "outDir": "./build",//编译文件输出目录
        "sourceMap": ture//是否生成.map文件,用于ts debug调试
    },
    "include": [
        "*/**/*.ts"
    ],
    "exclude": [
        //默认排除了node_modules
    ]    
}

为了能抛开在命令行中执行tsc命令,能直接通过vscode编辑器来编译,可以使用ctrl+shift+B快捷方式来编译ts文件。第一次使用会有提示

FrQK9PP8WHyeCNa6kM0Ie64IiN9Q

选择TypeScript - Watch-Mode,会在项目根目录下创建.vscode文件夹和tasks.json文件,内容如下:

vscode编译typescript配置

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "0.1.0",
    "command": "tsc",
    "isShellCommand": true,
    "args": ["-w", "-p", "."],
    "showOutput": "silent",
    "isWatching": true,
    "problemMatcher": "$tsc-watch"
}

这样就不用每次编译了,只要文件有修改就会自动编译

编译输出配置

为了将js文件输出到固定的目录,tsconfig.json中配置了"outDir": "./build"选项,会将ts文件编译到build目录下,得到了下面的文件目录结构:

.
├── app.ts
├── bin
│   └── www.ts
├── build
│   ├── app.js
│   ├── app.js.map
│   ├── bin
│   ├── public
│   ├── routes
│   ├── utils
│   └── views
├── node_modules
├── package.json
├── README.md
├── routes
│   ├── index.ts
│   ├── test.js
│   └── users.ts
├── test
├── tsconfig.json
├── typings
│   ├── globals
│   ├── index.d.ts
│   └── modules
├── typings.json
└── utils
    ├── cover.ts
    └── route.ts

build的目录下包含了public目录和views目录,这并不是编译生成的,因为最后执行的文件是js文件,为了与原来的express框架目录保持一致,手动将public和views等文件夹拷贝进去。

TypeScript中的装饰器Decorato什么时候执行?

月盾

ES6引入了类的概念,同时也引入了类似于java的注解概念,我们称之为装饰器,用于在某些场景下修改类和类成员。typescript要支持装饰器需要手动开启experimentalDecorators

命令行编译

tsc --target ES5 --experimentalDecorators /test.ts 或者配置文件设置:

tsconfig.json:

{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}

那么这个装饰器到底什么原理呢?它是什么时候执行的?用下面代码为例:

//test.ts
class Route {
    greeting: string;
    constructor(greet: string){
        this.greeting = greet;
    }

    @route("hello")
    default(): any {
        console.log(this.greeting);
    }
}

function route(name: string) {
    return function (target: Object, value: string, desc: PropertyDescriptor) {
        console.log('>>>>', name);
    }
} 

生成的js代码:

//test.js
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var Route = /** @class */ (function () {
    function Route(greet) {
        this.greeting = greet;
    }
    Route.prototype.default = function () {
        console.log(this.greeting);
    };
    __decorate([
        route("hello")
    ], Route.prototype, "default", null);
    return Route;
}());
function route(name) {
    return function (target, value, desc) {
        console.log('装饰器输出', name);
    };
}

备注

使用typescript开发nodejs的环境搭建(一)

月盾

在一切开始之前先具备的开发环境:

  • nodejs4.0+

  • 推荐的开发工具vscode

我希望通过本文介绍能直接做出一个最简单的项目框架,以便日后参考,这也是我想把之前纯nodejs写的博用typescript客重写一遍。

还是以expressjs为框架来搭建

使用express-generator来生成项目基本框架,需要全局安装

npm install -g express-generator

express -e mpro

参数-e是以ejs为模板引擎,mpor为项目目录名,最后生成这样的目录结构:

. 
├── app.js 
├── bin 
│ └── www 
├── package.json 
├── public 
│ ├── images 
│ ├── javascripts 
│ └── stylesheets 
│ └── style.css 
├── routes 
│ ├── index.js 
│ └── users.js 
└── views 
├── error.ejs 
└── index.ejs 

然后是全局安装typescript,便于在任何目录下编译,也可以安装在项目目录下。

npm install -g typescript

安装完以后初始化typescript:

tsc –init

会在根目录下生成tsconfig.json文件,用于配置ts的编译选项。 有了基本目录以后就可以着手编写代码了,为了使用ts开发,可以手动将routes目录下的js文件 和app.js后缀名改成.ts. 做完以上步骤以后可以试着在项目根目录下执行> tsc命令,可能什么都没发生, 这个命令会默认编译当前目录下的ts文件,但是我们项目中并没有这样的文件, 你可以自己新建一个后缀为.ts的文件再执行> tsc还是什么都没有发生一样,但是如果我们把app.ts (已经改过后缀的app.js文件)中的

使用半年TypeScript后的感受

月盾

初识TypeScript

从入职新公司开始算,使用typescript已经有半年多了,这是一个创业公司使用nodejs开发的后台业务管理系统,已经做的很大了,支撑着公司每月千万人民币的业务处理。很多怀疑nodejs是否可以开发后台管理,作为一个过来人告诉你可以放心使用,现在的javascript早已不是10年前用来写前端特效的js了。美中不足的是nodejs对报表这类数据处理量较大的功能有点力不从心,经常拖垮系统,当然也不是完全不能用nodejs来处理报表,可以合理的使用子进程单独处理数据而不影响主进程。但还是不推荐。

使用感受

其实起初对于typescript开发nodejs我是拒绝的,原因是建立起这个项目的人都走了,而这个项目结构实在是复杂,在windows下环境搭建费了好大精力,尤其是为了使用一个bcrypt加密包,需要安装Python,C++来编译,还不能使用nodejs0.12以上版本,最烦人的是每次修改文件后编译速度非常慢,这让我重新体验了开发JAVA时期的痛苦。而且服务启动方法也是前人自己编写shell脚本来同时编译前端文件和后端文件。并没有使用nodemon,supervisor这种监控工具。后来折腾发现可以直接使用vscode单独编译ts文件,一般12秒就编译完成,这与我之前的项目1秒修改重启相比还是慢了很多,不过也免强只能接受,此时只觉得如此麻烦和JAVA开发有什么区别!所以基于这些不好的体验我很不喜欢typescript。

重新认识

最近又折腾了下我用纯nodejs开发的博客,突然发现,写起来确实不如typescript爽,主要是纯nodejs在webstorm和vscode上语法提示和错误提示较弱,而typescript则可以很好的提示并且在开发过程中就能发现一些很低级错误,不用等到运行时才发现。尤其是对ES6的支持比较完整,甚至可以使用async,await这些ES7提案中的特性。虽然现在高版本的nodejs已经支持一些新特性,但是并不是所有项目都可以使用高版本。如果说为什么要使用typescript,那就是typescript集合了js的快速和强类型语言的安全稳定,用于开发一些较大的项目绝对利大于弊。 尤其是对于一些多人合作的项目来说,typescript的强类型能够很好约束对象属性,参数传递,对后续的重构也起到至关重要的作用。 有人可能会担心降低开发效率,这一点大可不必担心,你定义类型消耗的时间会从强类型提示上补回来的。 接下来会写一篇完整的环境搭建教程:使用typescript开发nodejs的环境搭建(一)