想学习 TypeScript 的小伙伴看过来,本文将带你一步步学习 TypeScript 入门相关的十四个知识点,详细的内容大纲请看下图:
一、TypeScript 是什么
TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。
TypeScript 提供最新的和不断发展的 JavaScript 特性,包括那些来自 2015 年的 ECMAScript 和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立健壮的组件。下图显示了 TypeScript 与 ES5、ES2015 和 ES2016 之间的关系:
1.1 TypeScript 与 JavaScript 的区别
TypeScript | JavaScript |
---|---|
JavaScript 的超集用于解决大型项目的代码复杂性 | 一种脚本语言,用于创建动态网页。 |
可以在编译期间发现并纠正错误 | 作为一种解释型语言,只能在运行时发现错误 |
强类型,支持静态和动态类型 | 弱类型,没有静态类型选项 |
最终被编译成 JavaScript 代码,使浏览器可以理解 | 可以直接在浏览器中使用 |
支持模块、泛型和接口 | 不支持模块,泛型或接口 |
支持 ES3,ES4,ES5 和 ES6 等 | 不支持编译其他 ES3,ES4,ES5 或 ES6 功能 |
社区的支持仍在增长,而且还不是很大 | 大量的社区支持以及大量文档和解决问题的支持 |
1.2 获取 TypeScript
命令行的 TypeScript 编译器可以使用 Node.js 包来安装。
1.安装 TypeScript
$ npm install -g typescript
2.编译 TypeScript 文件
$ tsc helloworld.ts # helloworld.ts => helloworld.js
当然,对于刚入门 TypeScript 的小伙伴,也可以不用安装 typescript
,而是直接使用线上的 TypeScript Playground 来学习新的语法或新特性。
TypeScript Playground:www.typescriptlang.org/play/
十三、TypeScript 装饰器
13.1 装饰器是什么
- 它是一个表达式
- 该表达式被执行后,返回一个函数
- 函数的入参分别为 target、name 和 descriptor
- 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
13.2 装饰器的分类
- 类装饰器(Class decorators)
- 属性装饰器(Property decorators)
- 方法装饰器(Method decorators)
- 参数装饰器(Parameter decorators)
13.3 类装饰器
类装饰器声明:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction
) => TFunction | void;
类装饰器顾名思义,就是用来装饰类的。它接收一个参数:
- target: TFunction - 被装饰的类
看完第一眼后,是不是感觉都不好了。没事,我们马上来个例子:
function Greeter(target: Function): void {target.prototype.greet = function (): void {console.log("Hello Semlinker!");};
}@Greeter
class Greeting {constructor() {// 内部实现}
}let myGreeting = new Greeting();
myGreeting.greet(); // console output: 'Hello Semlinker!';
上面的例子中,我们定义了 Greeter
类装饰器,同时我们使用了 @Greeter
语法糖,来使用装饰器。
友情提示:读者可以直接复制上面的代码,在 TypeScript Playground 中运行查看结果。
有的读者可能想问,例子中总是输出 Hello Semlinker!
,能自定义输出的问候语么 ?这个问题很好,答案是可以的。
具体实现如下:
function Greeter(greeting: string) {return function (target: Function) {target.prototype.greet = function (): void {console.log(greeting);};};
}@Greeter("Hello TS!")
class Greeting {constructor() {// 内部实现}
}let myGreeting = new Greeting();
myGreeting.greet(); // console output: 'Hello TS!';
13.4 属性装饰器
属性装饰器声明:
declare type PropertyDecorator = (target:Object, propertyKey: string | symbol ) => void;
属性装饰器顾名思义,用来装饰类的属性。它接收两个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 被装饰类的属性名
趁热打铁,马上来个例子热热身:
function logProperty(target: any, key: string) {delete target[key];const backingField = "_" + key;Object.defineProperty(target, backingField, {writable: true,enumerable: true,configurable: true});// property getterconst getter = function (this: any) {const currVal = this[backingField];console.log(`Get: ${key} => ${currVal}`);return currVal;};// property setterconst setter = function (this: any, newVal: any) {console.log(`Set: ${key} => ${newVal}`);this[backingField] = newVal;};// Create new property with getter and setterObject.defineProperty(target, key, {get: getter,set: setter,enumerable: true,configurable: true});
}class Person { @logPropertypublic name: string;constructor(name : string) { this.name = name;}
}const p1 = new Person("semlinker");
p1.name = "kakuqo";
以上代码我们定义了一个 logProperty
函数,来跟踪用户对属性的操作,当代码成功运行后,在控制台会输出以下结果:
Set: name => semlinker
Set: name => kakuqo
13.5 方法装饰器
方法装饰器声明:
declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol, descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;
方法装饰器顾名思义,用来装饰类的方法。它接收三个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 方法名
- descriptor: TypePropertyDescript - 属性描述符
废话不多说,直接上例子:
function LogOutput(tarage: Function, key: string, descriptor: any) {let originalMethod = descriptor.value;let newMethod = function(...args: any[]): any {let result: any = originalMethod.apply(this, args);if(!this.loggedOutput) {this.loggedOutput = new Array<any>();}this.loggedOutput.push({method: key,parameters: args,output: result,timestamp: new Date()});return result;};descriptor.value = newMethod;
}class Calculator {@LogOutputdouble (num: number): number {return num * 2;}
}let calc = new Calculator();
calc.double(11);
// console ouput: [{method: "double", output: 22, ...}]
console.log(calc.loggedOutput);
下面我们来介绍一下参数装饰器。
13.6 参数装饰器
参数装饰器声明:
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number ) => void
参数装饰器顾名思义,是用来装饰函数参数,它接收三个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 方法名
- parameterIndex: number - 方法中参数的索引值
function Log(target: Function, key: string, parameterIndex: number) {let functionLogged = key || target.prototype.constructor.name;console.log(`The parameter in position ${parameterIndex} at ${functionLogged} hasbeen decorated`);
}class Greeter {greeting: string;constructor(@Log phrase: string) {this.greeting = phrase; }
}// console output: The parameter in position 0
// at Greeter has been decorated
介绍完 TypeScript 入门相关的基础知识,猜测很多刚入门的小伙伴已有 “从入门到放弃” 的想法,最后我们来简单介绍一下编译上下文。
十四、编译上下文
14.1 tsconfig.json 的作用
- 用于标识 TypeScript 项目的根路径;
- 用于配置 TypeScript 编译器;
- 用于指定编译的文件。
14.2 tsconfig.json 重要字段
- files - 设置要编译的文件的名称;
- include - 设置需要进行编译的文件,支持路径模式匹配;
- exclude - 设置无需进行编译的文件,支持路径模式匹配;
- compilerOptions - 设置与编译流程相关的选项。
14.3 compilerOptions 选项
compilerOptions 支持很多选项,常见的有 baseUrl
、 target
、baseUrl
、 moduleResolution
和 lib
等。
compilerOptions 每个选项的详细说明如下:
{"compilerOptions": {/* 基本选项 */"target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'"lib": [], // 指定要包含在编译中的库文件"allowJs": true, // 允许编译 javascript 文件"checkJs": true, // 报告 javascript 文件中的错误"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'"declaration": true, // 生成相应的 '.d.ts' 文件"sourceMap": true, // 生成相应的 '.map' 文件"outFile": "./", // 将输出文件合并为一个文件"outDir": "./", // 指定输出目录"rootDir": "./", // 用来控制输出目录结构 --outDir."removeComments": true, // 删除编译后的所有的注释"noEmit": true, // 不生成输出文件"importHelpers": true, // 从 tslib 导入辅助工具函数"isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似)./* 严格的类型检查选项 */"strict": true, // 启用所有严格类型检查选项"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错"strictNullChecks": true, // 启用严格的 null 检查"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'/* 额外的检查 */"noUnusedLocals": true, // 有未使用的变量时,抛出错误"noUnusedParameters": true, // 有未使用的参数时,抛出错误"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)/* 模块解析选项 */"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)"baseUrl": "./", // 用于解析非相对模块名称的基目录"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容"typeRoots": [], // 包含类型声明的文件列表"types": [], // 需要包含的类型声明文件名列表"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。/* Source Map Options */"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性/* 其他选项 */"experimentalDecorators": true, // 启用装饰器"emitDecoratorMetadata": true // 为装饰器提供元数据的支持}
}