本文深入解析 TypeScript 在前端开发中的必要性,阐述其类型系统带来的编译期错误检查、编辑器智能提示及代码可维护性优势。同时提供从环境搭建、tsconfig.json 配置到 Webpack 集成的完整实战指南,帮助开发者快速上手 TypeScript。
TypeScript 与 JavaScript 代码具有极高的兼容性,迁移成本极低,直接将 JS 代码重命名为 TS 即可运行。对于未接触过强类型语言的开发人员而言,可能会担心定义类型和新概念会增加学习负担。但 TypeScript 并不会脱离 JavaScript 成为独立语言,其核心学习点在于掌握其类型系统。
什么是 TypeScript
TypeScript 是由微软推出的 JavaScript 超集语言。作为 JavaScript 的严格超集,所有合法的 JavaScript 代码均符合 TypeScript 规范,这类似于 C 与 Objective-C 的关系。TypeScript 为 JavaScript 引入了强大的类型系统以及对 ES2015 标准的支持,其编译工具可部署于任何服务器或任务系统中。
自 ES2015 发布以来,JavaScript 取得了显著进步。然而,随着设备性能提升及 JavaScript 在应用层地位的重要性日益凸显,大型项目对语言级别的支持需求愈发强烈。微软发现,外部客户在开发大规模 JavaScript 应用时,常受限于语言本身的短板,这促使了 TypeScript 的诞生。
类型系统不仅是良好的文档形式,还能增强编辑器在 智能提示、跳转定义 以及 代码补全 等方面的功能。它能在编译阶段捕获大部分错误,对大型工程的代码可读性和可维护性具有显著作用。
TypeScript 的流行趋势
TypeScript 拥有活跃的社区生态,大部分第三方库都提供了 TypeScript 类型定义文件,甚至知名前端库如 Google 的 Angular 已完全使用 TypeScript 开发。我们可以通过相关数据观察其流行趋势:

TypeScript 的优势和收益是什么
虽然 TypeScript 官网列出了诸多优势,但此处尝试进行总结:
- 类型系统可在编译阶段发现大部分错误
- 类型系统也是直观的编程文档,可清晰查看任何函数或 API 的输入输出类型
- 类型系统增强了编辑器或 IDE 的功能
- TypeScript 支持自动类型推导
- 所有 JavaScript 代码均为合法的 TypeScript,降低了使用门槛
- TypeScript 积极拥抱 ES2015 及 ESNext 草案规范
- 几乎所有第三方库都提供了 TypeScript 类型定义文件
对于需要长期维护的工程,类型系统在可读性和可维护性上展现出比 JavaScript 更强的能力。在良好的编程语境和稳定工具链的辅助下,TypeScript 堪称目前最佳的选择之一。
当然,TypeScript 也存在一定的学习成本,例如 Interfaces、Generics、Enums 等前端工程师可能不熟悉的概念,短期内会增加开发成本。集成和构建库时也需要一定工作量。例如使用 React 开发前端工程时,需进行相关配置,也可直接使用 create-react-app 创建 TypeScript + React 工程。
TypeScript 与 JavaScript 对比表格
| 对比项目 | TypeScript | JavaScript | 注意 |
| --------| --- | --- | --- |
| 基本类型 | boolean number string Array Tuple Enum any void null undefined never object | string number boolean null undefined symbol | TypeScript 中 object 表示的是不是 JavaScript 基本类型的类型|
| 变量声明 | let const var| let const var | 基本一致 |
| 接口 | interface | 无 | TypeScript 的核心是类型检查,因此接口充当了命名这些类型的角色 |
| 类 | class abstract class readonly … | class 无 abstract class | 基本一致,但不同的可能发生在未来,TypeScript 使用 private 来定义私有,而 JavaScript 未来极有可能将 #.xx 来定义私有写入标准。TypeScript 支持抽象类,只读等等。|
| 函数 | N | N | 基本一致,参数赋默认值,剩余参数等等,唯一不同的是 TypeScript 支持?可选参数 |
| 泛型 | Generics | 无 | 泛型是一个特别灵活的可重用指定不同类型来控制具体类型的类型,TypeScript 支持 |
| 枚举 | Enums | 无 | TypeScript 支持的枚举不仅可以默认从 0 开始,也可以赋值具体的字符串,它的操作空间非常大 |
| 类型推断 | 支持 | 无 | let x = 3; TypeScript 可以通过 3 来推断 x 的类型是 number |
| 高阶类型 | & typeof instanceof … | 无 | TypeScript 独有 |
| Symbols | N | N | Symbol 一样 |
| 迭代器 | N | N | 如果实现了 Symbol.iterator ,那么就被视为可迭代的,术语上和 JavaScript 定义的一样 |
| Generators | N | N | 一样 |
| 模块系统 | N | export import | 事实上 TypeScript 支持多种多样的模块系统,既有 ESModule 也有 Commonjs 规范,甚至还有 AMD UMD 等 |
| 其他 | N | N | 由于 TypeScript 是 JavaScript 的超集,因此 ES2016 之后以及 ESNext 定义的 api 都可以直接在 TypeScript 中使用 并不需要语言支持,至于其他一些比如 JSX Mixins 等等,由于这些不属于 JavaScript 标准因此这里不再复述 。|环境配置
TypeScript 3.3
鉴于 TypeScript 需要编译,我们必须使用编译工具。在正式学习前,需先安装编译环境。
- 安装 Node.js 10.15.3 LTS
- 安装 typescript
$ npm install -g typescript
$ tsc --version国际惯例 Hello World
创建一个 helloworld.ts 文件,然后输入:
let helloworld = "";
console.log(helloworld);
$ tsc helloworld.ts
现在稍微改动一行代码,在 JavaScript 中完全可以这样写:
let helloworld = "";
helloworld = 1;
从语法角度看这完全正确,但语句未锁定类型,某些情况下易出现未知异常。现在执行 tsc 来编译它:
$ tsc helloworld.ts
helloworld.ts:5:1 - error TS2322: Type '1' is not assignable to type 'string'.
5 helloworld = 1;
Found 1 error.编译器明确提示不能将 1 赋值给字符串类型,这正是 TypeScript 带来的优势。
tsconfig.json
tsconfig.json 文件用于指定编译 TypeScript 项目所需的根目录及编译器选项。若工程中存在 tsconfig.json,则表明 tsconfig.json 所在目录为 TypeScript 工程的根目录。
使用 tsconfig.json 的规则如下:
- 若直接运行
tsc,编译器将从当前目录开始搜索tsconfig.json并向上查找 - 也可直接运行
tsc -p xxx,此时 xxx 目录上必须存在tsconfig.json文件 - 若在命令行直接输出文件
tsc helloworld.ts,编译器将忽略tsconfig.json文件
当然,也可直接使用 tsc --init 在当前运行目录中创建一个 tsconfig.json(推荐)。
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 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 */
// "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. */
/* 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. */
}
}编译器选项
这些编译器选项也是 tsconfig.json 中的配置项
若不想通过 tsconfig.json 配置编译器,也可使用如下命令行选项执行编译任务。此处仅介绍常用选项,其他配置请参考 Compiler Options 表格。
--watch 或 -w监视输入文件的更改并触发重新编译--version 或 -v查看编译器的版本--types=["字符串"]类型描述文件的名称列表--target=""指定编译的 ECMAScript 的版本,默认版本是 ES3--strict启用所有严格类型检查--sourceMap生成对应的 .map 文件--outFile输出到单个文件--outDir输出到目录--module 或 -m指定使用哪种模块标准来输出代码,选项有 "None", "CommonJS", "AMD", "System", "UMD", "ES6", "ES2015" or "ESNext"--lib要包含在编译中的库文件列表,比如 ES2015.Promise--jsx在.tsx文件中支持JSX可指定要编译输出的JSX类型
与 Webpack 集成
实际上,我们不会单独使用 TypeScript,而是将其融入现有技术栈和工具中。目前多数前端工程将 Webpack 视为标准配置,因此 TypeScript 编译器与 Webpack 的集成非常有用。
awesome-typescript-loader 是 TypeScript 提供的在 Webpack 中使用的 loader,因此我们只需要:
$ yarn add typescript awesome-typescript-loader source-map-loader --dev然后在你的 webpack.config.json 文件中配置即可:
var fs = require('fs')
var path = require('path')
var webpack = require('webpack')
const { CheckerPlugin } = require('awesome-typescript-loader');
var ROOT = path.resolve(__dirname)
module.exports = {
entry: './src/index.tsx',
devtool: 'inline-source-map',
output: {
path: ROOT + '/dist',
filename: '[name].bundle.js',
sourceMapFilename: '[name].bundle.map.js',
publicPath: '//localhost:8889/dist/',
},
devServer: {
inline: true,
quiet: true,
contentBase: "./",
port: 8889
},
module: {
rules: [
{ test: /\.ts[x]?$/, loader: "awesome-typescript-loader" },
{ enforce: "pre", test: /\.ts[x]$/, loader: "source-map-loader" },
]
},
resolve: {
extensions: [".ts", ".tsx"],
alias: {
'@': path.resolve(ROOT,'src')
}
},
plugins: [
new CheckerPlugin(),
]
}
结语
本文简要介绍了如何学习 TypeScript。古语云:了解历史才能真正了解故事。时代变迁中,JavaScript 是目前唯一实现 Write Once Run Anywhere 的脚本语言(尽管 C 语言也具备此特性),其热度长久不衰。但 JavaScript 本身存在缺陷,未来新标准或许会逐步弥补。至少现在,让我们用 TypeScript 来解决你可能面临的问题。
到此这篇关于 typescript 难学吗?前端有必要学?该怎么学 typescript 的文章就介绍到这了,更多相关前端学 typescript 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!