概述

TypeScript是一门基于JavaScript基础之上的编程语言,很多时候我们都在说,他是一个JavaScript的超集或者叫扩展集。

所谓超级其实就是在JavaScript原有基础之上多了一些扩展特性,那多出来的实际上就是一套更强大的类型系统,以及对ECMAScript的新特性的支持。

那他最终会被编译为原始的JavaScript,那这也就是说我们去使用TypeScript过后,我们开发者在开发过程当中就可以直接去使用TypeScript所提供的这些新特性,以及我们在TypeScript当中他有更强大的类型系统,然后去完成我们的开发工作。

那在完成开发工作过后呢,我们再将我们的代码编译成能够在生产环境直接去运行的JavaScript代码,那他的作用也就非常明显了。

那TypeScript当中的类型系统的优势呢其实我们在之前已经有所体会了,因为他跟Flow当中是类似的。无外乎就是帮我们避免我们在开发过程中有可能会出现的类型异常,然后去提高我们编码的效率,以及我们代码的可靠程度。

那对于新特性的支持呢也不用多说,因为ECMAScript在近几年迭代了很多非常有用的新功能,但是很多时候在一些陈旧的环境中都会有一些兼容性的问题,那TypeScript他就支持自动取转换这些新特性,所以说我们就可以立即去使用这些新特性,在TypeScript当中。

那也就是说,即便说我们不需要类型系统,就是我们不想要这种强类型系统,那通过TypeScript去使用ECMAScript的新特性,也是一个很好的选择。

那我们之前都是使用babel去转换我们JavaScript当中的一些新特性,那其实TypeScript在这方面跟babel实际上是类似的,因为TypeScript最终他可以选择最低应该是能编译到ECMAScript3版本的代码,所以说他的兼容性特别的好。

另外,因为TypeScript最终编译成JavaScript去工作,所以说任何一个JavaScript运行环境下的运行程序都可以使用TypeScript去开发,例如我们传统的浏览器应用或者是node应用,或者是rn,也或者是桌面应用。他们都可以使用TypeScript去开发。

那相比较于之前所介绍的Flow,那TypeScript呢他作为一门完整的编程语言,那他的功能要更为强大,而且还有一个很重要的点,他的生态更加健全更加完善,特别是对于开发工具这一块,那微软自家的开发工具对TypeScript的支持都特别友好。

像我们之前在vscode当中去使用Flow的话你会感觉有些迟钝有些卡,但是呢你去使用TypeScript的话,你会感觉非常的流畅,那目前有很多大型的开源项目都已经开始使用TypeScript去开发了,那最知名的当然也就是谷歌额angular和vue3.0。

慢慢的你会发现TypeScript已经可以说是前端领域当中的第二语言,那如果说你是小项目,需要灵活自由,那自然会选择JavaScript本身,相反如果说你是长周期开发的大型项目,那所有的人都会建议你选择使用TypeScript。

当然了,再美好的东西他一般都会有些缺点,那TypeScript他最大的缺点就是这个语言本身多了很多概念,就是相比较JavaScript来说,例如像接口,泛型,枚举等等一系列这样的概念。

那这些概念就会提高我们去学习他的一个学习成本,不过还在TypeScript它属于渐进式的,就是即便是我们什么特性都不知道,我们也可以立马按照JavaScript的标准语法去编写TypeScript代码,说白了也就是可以完全把他当中JavaScript去使,然后我们在学习的过程中了解到了一个特性我们就可以使用一个特性。

那再者就是对于周期比较短的小型项目,TypeScript他有可能会去增加一些开发成本,因为我们在项目的初期我们回去编写很多的类型声明,比如说我们的这些对象,我们的这些函数,他都会有很多的类型声明,需要我们去单独的编写。

那如果说是一个长期维护的大型项目的话那这些成本根本不算什么,而且很多时候都是一劳永逸的,那这样的话,他给我们整体带来的优势呢,实际上是远大于这点小问题的。

所以整体上来讲的话,TypeScript是我们前端这个行业再向后发展应该是一门必要的语言了。

快速上手

下面我们快速了解一下TypeScript的基本使用,那想要使用TypeScript的话,首先需要安装他,TypeScript本身就是一个npm的模块,可以选择安装到全局,但是考虑到项目依赖的问题我们这里安装到项目中,这样的话更加合理。

npm install typescript --save-dev

安装完成过后,这个模块在node_modules/.bin中就会多出一个tsc的命令,这个命令的作用就是用来帮我们去编译TypeScript代码。

那有了这个命令之后我们就可以去新建一个TypeScript文件,先去做一个最基本的尝试,这里我们新建一个started.ts文件,注意这里,TypeScript的扩展名呢默认就是ts。

在这样一个文件当中我们就可以使用TypeScript去编写代码了,不过在这里我们还没有去了解任何的TypeScript语法,那没有关系,我们之前说过了,因为TypeScript他是基于JavaScript基础之上的,所以我们完全可以按照JavaScript的标准语法在这里呢去编写代码。

而且由于TypeScript他支持最新的ECMAScript标准,所以说我们可以按照最新的标准去编码,那这里我们尝试使用const去定义一个hello,他的值是一个箭头函数,这个函数我们去接收一个name参数,然后在这个函数的函数体中我们是打印一下这个name参数。

完成以后要我们调用一下这个hello函数,传入一个字符串。

const hello = name => {
    console.log(`Hello, ${name}`);
}

hello('TypeScript');

那这样一串代码就是按照最新的ECMAScript的标准呢去编写的一段普通的JavaScript,并没有任何特殊的用法,我们这里直接去尝试使用TypeScript编译一下这个文件。

这里我们可以通过npx去找到tsc命令,传入入口文件路径。

npx tsc started.ts

完成过后呢在跟目录下就会多出一个同名的js文件,那我们可以打开这样一个js文件,那这时候你就会发现,我们这里所使用的所有的ES6部分都会被转换成标准的ECMAScript3的标准的一个代码。

那这也就印证了之前我们所说的,即便是我们不需要TypeScript所提供的类型系统,我们也可以使用TypeScript直接使用最新的ECMAScript标准,那这也可以说是TypeScript当中一个主要的功能。

那除了编译转换ES的一些新特性,那TypeScript更重要的呢就是,为我们提供了一套更强大的类型系统。

那我们可以再回到这个ts文件当中,那这里我们去使用这个类型系统的方式跟我们之前在Flow当中的一些方式基本上是完全相同的。

例如我们这里需要限制这个name参数是string类型,那我们同样可以在他的后面添加一个:string,这样的一个类型注解。

那此时如果我们在外界调用时传入的不是一个字符串,而是一个别的数据类型,那这个时候我们再次去编译,就会报出语法错误。

const hello = (name: string) => {
    console.log(`Hello, ${name}`);
}

// hello('TypeScript');
hello(100);

而且呢,我们的vscode默认就支持对TypeScript的语法做对应的类型检查,所以说我们这不用等到编译,在编辑器当中我们就可以直接看到所有的错误提示。

那这里我们尝试编译一下这个文件。

npx tsc started.ts

那此时我们编译的这个过程也会报出对应的错误,当我们将100改成字符串的时候就会编译完成通过。

这时候我们打开输出的js文件可以发现,我们之前添加的一些类型注解,同样被移除掉了。

最后我们再来总结一下我们这里使用TypeScript的一个基本过程。

那首先呢我们应该先安装一个TypeScript模块,那这个模块呢他提供了一个叫做tsc的命令,这个tsc实际上就是typescript-compare,我们通过这样一个命令的作用呢就是去编译TypeScript文件。

那在编译过程中呢,TypeScript首先会先去检查我们代码中的类型使用异常,然后会去移除掉我们一些类型注解之类的一些扩展的语法,并且在这个过程中他还会自动转换ECMAScript的新特性。

那这就是TypeScript的一个基本使用。

配置文件

TypeScript支持通过一个叫做tsconfig.json的文件作为配置文件,我们可以通过tsc --init生成TypeScript的配置文件,一般来说,tsconfig.json文件所处的路径就是当前项目的根路径。

tsc --init

可以发现,在根目录中多了一个tsconfig.json文件,我们打开这个文件来看一下。

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    // "incremental": true,                   /* Enable incremental compilation */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    // "lib": [],                             /* Specify library files to be included in the compilation. */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    // "outDir": "./",                        /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
    // "removeComments": true,                /* Do not emit comments to output. */
    // "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */

    /* Advanced Options */
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

compilerOptions用来配置编译选项,可以发现里面很多内容被注释掉了,而且在每个选项上都会配有一些简要的说明。

那这里呢我们可以先来看几个最常用的到的选项。

首先是这里的一个target属性,那这个选项他的作用就是用来去设置编译后的JavaScript所采用的ECMAScript标准,那这里目前配置的是ES5, 也就是说在我们的代码当中会把所有的一些新特性都会转换成ES5标准的代码。

那我们可以尝试把他修改为ES2015, 那这样的话在我们的输出结果当中就不会再去转换ES6的特性了。

"target": "es2015",      

然后在下面是一个module的选项,那这个选项指的就是我们输出的代码他会采用什么样的方式去进行模块化,当前这样一个配置选项是commonjs,也就是他会把导入导出的操作最终都编译成commonjs当中的那个require和module.export的方式。

再往下我们经常还会用到outDir,那这个用来设置编译结果输出到的文件夹,我们可以设置为dist目录。

"outDir": "dist"

然后还有一个叫做rootDir, 那他的作用就是配置我们源代码,也就是TypeScript代码所在的文件夹,那一般我们都会把源代码放在src目录。

"rootDir": "src"

然后还有一个叫做sourceMap的选项,那这个选项我们可以尝试把他设置为true,也就是开启源代码映射,那这样的话我们在调试的时候就能够使用这个source-map文件去调试TypeScript的源代码了,非常的方便。

那再往下就是类型检查的一些相关配置了,那这里默认开启了一个strict,也就是开启了严格模式,那这个选项的作用就是开启所有严格检查选项,那在这种情况下,对于类型的检查会变得十分严格。

我们这里可以举一个例子,那我们删掉name参数对应的注解,那这里的name参数就会被隐式推断为Any类型,那这种用法在严格模式下是不被允许的。

严格模式下他需要我们为每一个成员去指定一个明确的类型,即便他是Any,那也需要明确去指定,不能隐式推断。

// const hello = (name: string) => {
const hello = (name) => {
    console.log(`Hello, ${name}`);
}

hello('TypeScript');

当然这里还有很多其他的配置选项,那这些其他的配置选项我们到用到的时候再去详细介绍。

那有了这个配置文件过后呢我们再去使用tsc这个命令的时候我们就可以去使用这个配置文件了。

但是这里我们需要注意的是,如果我们还是使用tsc命令去编译某一个指定的文件,那这里的配置文件是不会生效的。

npx tsc started.ts

那只有我们直接运行tsc这样一个命令编译整个项目的时候, 我们这个配置文件呢他才会自动生效。

npx tsc

但是我们运行这个命令的时候,结果确报出了一个错误,说的是我们这里的文件并没有放置在我们配置的src这样一个目录当中,那这也就证明了我们的配置文件已经开始工作了。这里我们修改一下,将started.ts移动到src目录中。对于之前生成的js文件我们这里删除掉。

那我们再次运行tsc命令就会按照我们的配置将src目录下所有的TypeScript文件编译到dist当中了。

那我们可以打开输出的dist目录,那这里不仅生成了编译过后的js文件,还有对应的source-map文件,而且我们这里所生成的js文件他是按照ES6的标准去输出的。

那这就是我们对配置文件的一个简单的了解,以及我们使用了配置文件过后我们应该怎样去在运行这个命令的时候让他工作。


欢迎关注,更多系列文章