TypeScript入门笔记(三)
接上篇。
高级类型
1 /*===============================类型断言======================================*/ 2 /*C#中可以使用as或者强转的方法,虽然ts中也有类似的写法,但是类型断言 3 只会影响 TypeScript 编译时的类型,类型断言语句在编译结果中会被删除*/ 4 5 //基接口 6 class Animal { 7 constructor(readonly name: string) { 8 } 9 eat(): void { 10 console.log(`${this.name} is eating`); 11 }; 12 } 13 //子接口们 14 class Dog extends Animal { 15 roar(): void { 16 console.log(`${this.name} is wongwongwong`); 17 }; 18 } 19 class Bird extends Animal { 20 fly(): void { 21 console.log(`${this.name} is flying`); 22 }; 23 } 24 class Fish extends Animal { 25 swim(): void { 26 console.log(`${this.name} is swimming`); 27 }; 28 } 29 //参数使用父类可以接收所有子类 30 function letsEat(animal: Animal) { 31 animal.eat(); 32 } 33 letsEat(new Dog("小鱼")); //小白 is eating 34 35 // 在ts中,父类可以断言成子类,因为“狗狗肯定是动物”很好理解 36 function dog2Animal(xb: Dog) { 37 (xb as Animal).eat() 38 } 39 40 //函数参数如果使用联合类型,那么可以调用其动物类的方法 41 function letsEat2(animal: Dog | Bird | Fish) { 42 animal.eat(); 43 } 44 letsEat(new Fish("小白")); //小白 is eating 45 46 //那么想调用子类的方法的话,我们可以先进行类型断言,父类可以被断言为为子类! 47 //?:那如果animal断言成Dog,但是animal的实际类型不是Dog,那会怎么样呢? 48 function letRoar(animal: Animal) { 49 (animal as Dog).roar(); 50 } 51 function letsFly(animal: Dog | Bird | Fish) { 52 (animal as Bird).fly(); 53 } 54 //结果就是ts编译没问题,但是实际运行会报错 55 //letRoar(new Fish("小鱼")); //Error: animal.swim is not a function 56 //letsFly(new Dog("小白")); //Error: animal.fly is not a function 57 58 //解决这个情况可以使用intanceof方法先判断 59 function letsPlay(animal: Dog | Bird | Fish) { 60 if (animal instanceof Dog) { 61 (animal as Dog).roar(); 62 } 63 else if (animal instanceof Bird) { 64 (animal as Bird).fly(); 65 } else if (animal instanceof Fish) { 66 (animal as Fish).swim(); 67 } 68 } 69 letsPlay(new Bird("小鸟")); //小白 is flying 70 71 // 总结(https://ts.xcatliu.com/basics/type-assertion) 72 // (1)联合类型可以被断言为其中一个类型; 73 // (2)父类可以被断言为子类; 74 // (3)任何类型都可以被断言为 any; 75 // (4)any 可以被断言为任何类型; 76 // 总之,若 A 兼容 B,那么 A 能够被断言为 B,B 也能被断言为 A 77 // 注意:类型断言不是类型转换 78 79 80 /*===============================交叉类型======================================*/ 81 //交叉类型是将多个类型合并成一个类型的意思 82 //例如, Person & Serializable & Loggable 这个类型的对象同时拥有了这三种类型的成员。 83 84 /*for-in语法:可以遍历一个对象中的所有属性,并以字典的读取方式获取属性的值,语法: 85 for (const prop in object) { 86 if (object.hasOwnProperty(prop)) { 87 const element = object[prop]; 88 } 89 } 90 */ 91 92 //如何创建混入的一个方法 93 function extend<T1, T2 extends object>(type1: T1, type2: T2): T1 & T2 { 94 let result = <T1 & T2>{}; 95 96 for (let prop in type1) { 97 (<any>result)[prop] = type1[prop]; 98 } 99 100 for (let prop in type2) { 101 if (type2.hasOwnProperty(prop)) { 102 (<any>result)[prop] = (<any>type2)[prop] 103 104 } 105 } 106 return result; 107 } 108 //在创建一个类类型 109 class Identity { 110 constructor(public gender: string, public birthDay: Date) { 111 } 112 } 113 //将Dog类和Identity类进行交叉 114 let bd: Date = new Date('2015-09-07'); 115 let xb = extend(new Dog("小白"), new Identity("male", bd)); 116 117 console.log(xb.birthDay); //2015-09-07 118 console.log(xb.gender); //male 119 xb.roar(); //小白 is wongwongwong 120 121 122 /*==============================可以为null的类型================================*/ 123 //默认情况下,类型检查器认为 null与 undefined可以赋值给任何类型。 124 //按照JavaScript的语义,TypeScript会把 null和 undefined区别对待 125 126 let s = "foo"; 127 //s = null; // 错误, 'null'不能赋值给'string' 128 129 let sn: string | null = "bar"; 130 sn = null; // 可以 131 132 //sn = undefined; // error, 'undefined'不能赋值给'string | null' 133 134 //对于可为null类型的参数 135 function f(sn: string | null): string { 136 if (sn == null) { 137 return "default"; 138 } 139 else { 140 return sn; 141 } 142 } 143 /* “短路运算符”写法: 144 function f(sn: string | null): string { 145 return sn || "default"; 146 } 147 */ 148 149 150 /*==============================字符串字面量类型================================*/ 151 //字符串字面量:用来约束取值只能是某几个字符串中的一个。 152 153 type pet = 'dog' | 'bird' | 'fish'; 154 function adoptPet(p: pet): Animal { 155 if (p === 'dog') { 156 return new Dog("小白"); 157 } 158 else if (p === 'bird') { 159 return new Bird("飞飞"); 160 } 161 else if (p === "fish") { 162 return new Fish("金金"); 163 } 164 else { 165 assertNever(p); //这招叫“完整性检查”,自己体会,惊喜的是,如果前面条件判断有漏掉某个情形,这里是会报错的。 166 } 167 } 168 function assertNever(x: never): never { 169 throw new Error("Unexpected object: " + x); 170 } 171 172 adoptPet("bird"); //输入的参数只能是“dog”“fish”“bird”之一 173 //adoptPet("cat"); //Error: 传入其他值会报错 174 175 //上面的例子用switch-case写代码看起来更清晰简练一点 176 function adoptPetAgain(s: pet) : Animal { 177 switch (s) { 178 case "dog": return new Dog("小白"); 179 case "bird": return new Bird("飞飞"); 180 case "fish": return new Fish("金金"); 181 default: return assertNever(s); 182 } 183 }