# TypeScript - TypeScript - 加强版的Javascript,让JS更加安全 - JS拥有的特性,TS都支持,紧随ECMAScript标准 - 增加了类型约束,同时扩展一些语法:枚举、元组等 - TS在实现新特性的同时,保持和ES标准的同步 - TS最终会被编译成JS代码,不用担心兼容性 - **错误出现的越早越好** - 能在**写代码的时候**发现的错误,就不要**代码编译时**发现 - 能在**编代码编译期**发现的错误,就不要在**代码运行时**发现 - 能在**开发阶段**发现的错误,就不要在**测试阶段**发现错误 - 能在**测试阶段**发现的错误,就不要**上线后**才发现 ## 类型 ### 数据类型 | 类型 | 作用 | | ------ | ------------------------------ | | string | Typescript中的字符串类型 | | String | Javascript的字符串包装类的类型 | | number | Typescript中的数字类型 | | Number | JavaScript的数字包装类的类型 | | 类型 | 作用 | - | | --------------- | ----------------------------------------------------------------------------------- | -------------- | | number | 数字类型,不区分int和double,统一为number | 基本数据类型 | | boolean | 仅又true和false | 基本数据类型 | | string | 字符串类型,可以用单引号或双引号表示 | 基本数据类型 | | Array | 数组类型(最好类型固定,不要数组中存放多种类型) | 非基本数据类型 | | null、undefined | | | | Symbol | 符号 | | | any | 无法确定变量的类型,并且可能会发生改变的时候使用any | TS特有,JS没有 | | unknown | 用于描述类型不确定的变量,unknown只能赋值给unknown,any可以赋值给任意类型 | TS特有,JS没有 | | void | 通常用来指定函数没有返回值,那么返回值就是void类型,可以把null和undefined赋值给void | TS特有,JS没有 | | never | 永远不会发生的值,如果函数死循环或者抛出异常就不会返回任何值包括void | TS特有,JS没有 | | 联合类型 | `let n = string | number` 用 `|` 来表示`n`可以是`string`也可以是`number`类型 | | | tuple | 元组类型 | | ```typescript let num : number = 123; num = 222; let num1 : number = 100; // 十进制 let num2 : number = 0b111; // 二进制 let num3 : number = 0o234; // 八进制 let num4 : number = 0x29abc; // 十六进制 // -------------------------------------------------------------- let flag : boolean = true; flag = (20 > 40); // -------------------------------------------------------------- let name = "2345"; let name1 = `name : ${name}`; // 格式化字符串 // -------------------------------------------------------------- let names = []; // 默认是any类型数组 let names1 : Array = [];// 不推荐,在react jsx中有冲突 let names2 : string[] = []; // 推荐 // -------------------------------------------------------------- let info = { name : "w", age : 1 } // -------------------------------------------------------------- let n1 : null : null; let n2 : undefined : undefined; // -------------------------------------------------------------- let message : any = "hello"; // any运行时不会报错,如果是string就报错了 message = 123; let anyarray : any[] = []; // -------------------------------------------------------------- function sum(n1 : number, n2 : number) { console.log("hello"); } let resule = sum(1, 2); // void类型 // -------------------------------------------------------------- function loopFun() : never { while(true){ console.log("123"); } } function loopFun1() : never{ throw new Error(); } // 针对类型做特殊处理,此时忘记处理boolean,所以赋值给never值,此时会报错,做提示之用 function handleMessage(message : string | number | boolean) { switch(typeof message){ case 'string': break; case 'number': break; default : const check : never = message; } } handleMessage(true); // -------------------------------------------------------------- const info:any[] = ["q", 1, 1.0]; let val = info[0]; // val 为any类型,不够安全 const info2 : [string, number, numer] = ["x", 10, 1.0]; // 元组可以确定各个类型 // -------------------------------------------------------------- ``` ### 函数的参数类型 ```typescript function sum(num1: number, num2: number) : numer{ return num1 + num2; } ``` 和变量的类型注解一样,一般而言不需要编写返回类型注解,因为TypeScript会根据return返回值推断函数的返回类型(看个人喜好,编写了可读性更强) ```typescript const names = ["1", "2", "3"]; names.forEach(function(item) { }); ``` 一些成员函数可以不写数据类型,因为`names`是`string[]`,所以`item`肯定是`string`类型,Typescript会推导出来 ```typescript // 对象类型 function printPoint(point : {x: number, y:number, z?: number}){ } printPoint({ x: 1, y: 2}); printPoint({ x: 1, y: 2, z: 3 }); printPoint({ x: 1, y: 2, z: 3, q : 4 }); // Error ``` > z ?: number 表示可选数据类型,如果没有`z`打印是`undefined` `printPoint`目标是一个`Object`对象,该对象存在键`x`和`y`,并且都是`number`类型,目标对象可以存在名为`z`的key,也可以不存在`z` ### 组合类型 Typescript的类型系统允许我们使用多种运算符,从现有类型中构建新类型 - 一种组合类型:联合类型(Union Type) - 联合类型是由两个或者多个其他类型组成的类型 - 表示可以是这些类型中的任何一个值 - 联合类型中的每个类型被称之为联合成员(union's memeber) - 使用时必须小心类型 ```typescript // id 就是联合类型 function PrintID(id : number | string){ console.log(id); } ``` - 可选类型:可以理解为这个参数是 `目标类型 | undefined`的联合类型 ```typescript function foo(message ?: string){ console.log(message); } function foo1(message : string | undefined){ console.log(message); } foo1(undefined); ``` - 类型别名 ```typescript type UnionType = string | number | boolean; function PrintID(id : UnionType){ } type PointType = { x : number, y : number, z ?: number } funtion PrintPoint(point : PointType){ } ``` ### 类型断言 as 有时候TypeScript无法获取具体的类型信息,这个时候需要使用**类型断言**(Type Assertions) 通过类型断言,可以把普遍的类型转变成具体类型 > `document.getElementById`TS只知道返回HTMLElement,但并不知道具体类型 ```typescript const el = document.getElementById("img"); // 其实获取到的是img标签 el.src = "图片地址"; // 直接设置img标签的报错 const el1 = document.getElementById("img") as HTMLImageElement; // 其实获取到的是img标签 el1.src = "图片地址"; // 直接设置img标签的报错 class Person { } class Student extends Person { studying() { console.log("s"); } } function sayHello(p: Person) { (p as Student).studying(); } let s = new Student(); sayHello(s); // as 的奇淫巧计 别瞎用 const message = "hello"; const num : number = (message as any) as number; ``` ### 非空类型断言 ```typescript function printMessage(message ?: string){ console.log(message.length); } printMessage("hello"); printMessage(); // Error message是undefined的 function printMessage2(message ?: string){ console.log(message?.length); } ``` 上述代码`printMessage`不够严谨,`message`有`undefined`的可能 为了解决上述代码的不够严谨的问题,引入**可选链**(就是`printMessage2`中`.?`) ```typescript type Person = { name: string, friend?: { name: string, age?: number, girlFriend?: { name: string } } } function test(p: Person) { console.log(p.name); console.log(p.friend?.name); console.log(p.friend?.age); console.log(p.friend?.girlFriend?.name); } let info: Person = { name: "x", friend: { name: "y", girlFriend: { name: "z" } } } test(info); ``` 使用**可选链**可以省去复杂的`undefined`嵌套判断 > 是Javascript在ES11中添加的功能,非TS增加的功能 ### !!和??的作用 - !!操作符 - 将一个其他类型转换成`boolean`类型 - 类似`Boolean(变量)`的方式 - ??操作符 - 控制合并操作符是一个逻辑操作符 - 当操作符的左侧是null或者undefined是,返回其右侧操作数,否则返回左侧操作数 ```typescript const message = "hello"; const flag = Boolean(message); console.log(flag); const flag1 = !!message; console.log(flag1); const flag2 = (!(!message)); console.log(flag2); ``` > `(!!message)`的操作可以理解为两部分:`(!message)`将`message`转换成`boolean`类型并取反,再通过`!`把值转换回来 ```typescript const message = "321"; const result = message ?? "123"; console.log(result); ``` ### 字面量类型 TS中字面量类型的类型和值必须相同 ```javascript let message : "hello" = "hello"; let msg : 123 = 123; ``` > 通过上述代码可见,字符串也可以当作数据类型 虽然看起来不知道有什么用,但是配合联合类型就可以完成enum的功能 ```typescript type Alignment = 'left' | 'right' | 'center' | 'top'; let align : Alignment = 'left'; align = 'right'; type Method = 'Get' | 'Post'; function requests(url : string, method : Method){} let options = { url : "", method : "Post" } requests(options.url, options.method); // Error : options.method是字符串类型,不一定是"Post"/"Get" requests(options.url, options.method as Method); type Requested = { url: string, method: Method }; let options2 : Requested = { url : "", method : "Post" } requests(options2.url, options2.method); ``` > 通过字面量类型和联合类型,可以限制变量的值的内容 ### 类型缩小 - 类型缩小 - 类型缩小的英文:Type Narrowing - 通过类似`typeof padding === "number"`的判断语句,来改变TypeScript的执行路径 - 在给定的执行路径中,可以缩小比声明时更小的类型,过程称之为**缩小** - `typeof padding === "number"`称之为**类型保护(type guards)** - 常见的类型保 - typeof - 平等缩小(===, !==,==, !=, switch) - instanceof - in - ... > 就是逐渐缩小变量的类型的范围的过程 ```typescript // typeof function printID(id : number | string){ if (typeof id === 'string'){ // 从联合类型到确认为string类型 console.log(id.toUpperCase()); } else { // 从联合类型到确认为number类型 console.log(id); } } // 平等缩小 type Alignment = 'left' | 'right' | 'center' | 'top'; function printDirection(direction : Alignment){ switch(direction){ case 'left': break; case 'right': break; case 'center': break; case 'top': break; } } // instanceof 判断对象类型 function printTime(time : string | Date){ if (time instanceof Date){ console.log(time.toUTCString()); } else { console.log(string); } } // in type Fish = { swimming: () => void; } type Dog = { running: () => void; } function walk(animal : Fish | Dog){ if ('swimming' in animal){ // 判断为Fish类型 animal.swimming(); } else { animal.running(); } } ``` ### 函数类型 1. 函数作为参数时 ```javascript function foo(){ } function bar(fn : ()=>void){ } bar(foo); ``` > `·()=>void`是函数类型,不是匿名函数 2. 函数作为变量 ```typescript type AddFnType = (num1 : number, num2 : number) => number ; let add : AddFnType = (num1 : number, num2 : number) => { return num1 + num2; } ``` 3. 参数的可选类型 可选参数`name ?: type`必须写在最后,本质其实就是`name : type | undefined`,所以name其实可能为undefined,需要对name为undefined的情况做处理 ```typescript function foo(x: number, y?:number){ } ``` 4. 参数默认值 ```typescript function foo(x : number, y : number = 100){ console.log(x, y); } function foo1(x : number = 20, y : number){ console.log(x, y); } foo(20); foo1(undefined, 30); ``` 5. 剩余参数 使用`...nums : number[]`作为剩余参数 JS部分有讲 ```typescript function sum(...nums : number[]) : number{ if(nums.length == 1){ return nums[0]; } let val = nums[0]; nums = nums.slice(1); return val + sum(...nums); } console.log(sum(1, 2, 3, 4, 5, 6)); ```