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 }

 

posted @ 2020-04-19 23:19  冬瓜山总教练  阅读(199)  评论(0编辑  收藏  举报