一、TypeScript是什么
TypeScript 是拥有类型系统的javascript超集
可以编译成纯javascript
作用:静态类型检查
二、使用TypeScript的好处
1.在编辑器中提示更加友好,增加可维护性
在vscode中提示更加友好,鼠标放上去就可以看到对应的类型,而webstorm中只有书写的时候才能看到
2.提高代码质量,编译期间解决部分隐藏问题
-
类型传入错误
-
参数名称错误
-
参数个数错误
可以选参数在冒号前加一个?
3.接口代替文档
更适合于大型项目或框架类项目
三、搭建简单编译环境
1.全局安装typescript
npm install -g typescript
2.tsc命令编译文件
生成tsconfig.json文件,里边有些默认配置
tsconfig.json文件的具体配置项可以参考
TypeScript中文网-编译选项
tsc --init
添加xxx.ts文件
tsc xxx.ts
在命令行输入以上命令,可以看见生成了一个同名的xxx.js文件,这就是编译后的文件
四、基础类型
语法
(变量/函数):类型
let x:number = 1; // 变量 x必须是number类型
// 函数 参数 x 、y都需要接收number类型,同时返回值也是number类型
function add(x: number, y: number): number {
return x + y;
}
// 接口
interface LabelledValue {
label: string;
}
// 类
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
1.类型列表
es6 | TypeScript |
---|---|
boolean | boolean |
string | string |
number | number |
Array | Array |
Object | Object |
Function | Function |
Symbol | Symbol |
null | null |
undefined | undefined |
- | any |
- | void |
- | never |
- | 元祖 |
- | 枚举 |
- | 高级 |
2.Array
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
3.Object
Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
4.any
任何类型都可以被归为 any 类型
使用场景:
1.有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。
2.在对现有代码进行改写的时候,any类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。
注意:
如果我们使用 any 类型,就无法使用 TypeScript 提供的大量的保护机制。
5.void
某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void
function warnUser(): void {
console.log("This is my warning message");
}
声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null:
let unusable: void = undefined;
6. Never
never 类型表示的是那些永不存在的值的类型。 例如,never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
7.元祖
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
8. 枚举
enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
默认情况下,从0开始为元素编号。 你也可以手动的指定成员的数值。
1.数字枚举
enum DevStatus {
Online,
Offline,
UnRegister,
}
编译之后的代码
var Status;
(function (Status) {
Status[Status["Online"] = 0] = "Online";
Status[Status["Offline"] = 1] = "Offline";
Status[Status["UnRegister"] = 2] = "UnRegister";
})(Status || (Status = {}));
打印出来
可以通过值获取到名字,也可以通过名字获取到值
console.log(Status.Offline); // 1
console.log(Status[1]); // Offline
2.字符串枚举
enum Color {
Red = 'red',
Blue = 'blue'
}
编译后的代码
var Color;
(function (Color) {
Color["Red"] = "red";
Color["Blue"] = "blue";
})(Color || (Color = {}));
只能通过名字获取到值
3.异构枚举
异构枚举的成员值是数字和字符串的混合:
enum Enum {
A,
B = "B",
C = 3,
D,
}
编译后的代码
var Enum;
(function (Enum) {
Enum[Enum["A"] = 0] = "A";
Enum["B"] = "B";
Enum[Enum["C"] = 3] = "C";
Enum[Enum["D"] = 4] = "D";
})(Enum || (Enum = {}));
打印出来
五、接口
在面向对象语言中,接口是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类去实现。
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
interface Animal {
name: string,
readonly age: number, // 只读属性
sex?: number, // 可选属性
}
const dog: Animal = {
name: 'dog',
age: 2,
}
dog.age = 3; // Cannot assign to 'age' because it is a read-only property.ts(2540)
六、函数
1.剩余参数
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
2.函数重载
函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。要解决前面遇到的问题,方法就是为同一个函数提供多个函数类型定义来进行函数重载,编译器会根据这个列表去处理函数的调用。
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: number, b: string): string;
function add(a: any, b: any): any {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
console.log(1, 2); // 3
console.log('1', '2'); //12
console.log('1', 2); //12
console.log(1, '2'); //12
为了让编译器能够选择正确的检查类型,它与JavaScript里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。
注意,function add(a: any, b: any): any并不是重载列表的一部分,因此这里只有两个重载:一个是接收对象另一个接收数字。 以其它参数调用 add会产生错误。
七、泛型
function identity(arg: number): number {
return arg;
}
function identity(arg: string): string {
return arg;
}
function identity(arg: any): any {
return arg;
}
使用any类型会导致这个函数可以接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。
因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。
function identity<T>(arg: T): T {
return arg;
}
我们给identity添加了类型变量T。 T帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。
我们把这个版本的identity函数叫做泛型,因为它可以适用于多个类型。 不同于使用 any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。
八、联合类型和交叉类型
联合类型表示一个值可以是几种类型之一。 我们用竖线( |)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。
let a: string | number | boolean ;
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
interface Company {
cid: number;
}
interface Employee {
age: number;
name: string;
}
type workerType = Company & Employee;
const wroker: workerType = {
cid: 1,
age: 2,
name: 'chen'
}
九、装饰器
装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。
装饰器是一种函数,写成@ + 函数名。它可以放在类和类方法的定义前面。
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression
这种形式,expression
求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
上面代码中,@testable就是一个装饰器。它修改了MyTestableClass这个类的行为,为它加上了静态属性isTestable。testable函数的参数target是MyTestableClass类本身。
十、tsconfig.json
{
"compilerOptions": { //设置与编译流程相关的选项。
"target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"strictNullChecks": true, // 启用严格的 null 检查
"suppressImplicitAnyIndexErrors": true,
"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"importHelpers": true, // 从 tslib 导入辅助工具函数
"moduleResolution": "node", // 决定如何处理模块。或者是"Node"对于Node.js/io.js,或者是"Classic"(默认)。查看模块解析了解详情。
"experimentalDecorators": true, // 启用实验性的ES装饰器。
"esModuleInterop": true,
"allowSyntheticDefaultImports": true, // module === "system" 或设置了 --esModuleInterop 且 module 不为 es2015 / esnext esModuleInterop 且 module 不为 es2015 / esnext 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
"strictPropertyInitialization": false, // 确保类的非undefined属性已经在构造函数里初始化。若要令此选项生效,需要同时启用--strictNullChecks。
"sourceMap": true, // 生成相应的 .map文件。
"baseUrl": ".", // 用于解析非相对模块名称的基目录
"noImplicitThis": false, // 当 this 表达式值为 any 类型的时候,生成一个错误
"types": [ // 要包含的类型声明文件名列表。
"webpack-env"
],
"paths": { // 模块名到基于 baseUrl的路径映射的列表。
"@/*": [
"src/*"
]
},
"lib": [ // 指定要包含在编译中的库文件
"es6",
"es5",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [ // 设置需要进行编译的文件,支持路径模式匹配;
"src/**/*.ts",
"src/**/*.vue",
"node_modules/@types/node/globals.d.ts",
"node_modules/@types/node/globals.d.ts"
],
"exclude": [ // 设置无需进行编译的文件,支持路径模式匹配;
"node_modules"
]
}