TypeScriptz学习笔记
标签(空格分隔): TypeScript 撩课学院
安装TypeScript
cnpm install -g typescript
tsc -v // 查看版本
TypeScript初体验
1. 类型注解
function log(msg) {
console.log('hello' + msg);
}
log('itlike');
log(10);
2. 接口
interface Person {
name: string,
sex: string,
age: number
}
function logPerson(person: Person){
console.log(`姓名: ${person.name}, 性别: ${person.sex}, 年龄: ${person.age}`)
}
let xl = {
name: '小撩',
age: 25,
sex: '女'
}
logPerson(xl);
3. 类
class Dog {
dogName: string;
dogAge: number;
dogSex: string;
constructor(dogName: string, dogAge: number, dogSex: string){
this.dogName = dogName;
this.dogAge = dogAge;
this.dogSex = dogSex;
}
eat(foods: string){
console.log(this.dogName + '在吃' + foods)
}
}
let wc = new Dog('旺财', 6,'公');
console.log(wc);
wc.eat("蔬菜");
基础类型
1. 字符串
let dogName: string = '旺财';
let dogSex: string = '公';
let dogAge: number = 5;
let introDog: string = `
我有一只小狗,它叫${dogName},
它今年${dogAge}岁,它是
${dogSex}的
`
console.log(introDog);
2. 数字
// 2 8 10 16 进制
let num1: number = 16;
let num2: number = 0x10;
let num3: number = 0o20;
let num4: number = 0b10000;
console.log(num1, num2, num3, num4);
3. 布尔
let flag:boolean = false;
console.log(flag);
4. 数组
let numArr: number[] = [1,2,3];
let strArr: string[] = ['张三', '李四', '王五'];
console.log(numArr, strArr);
let boolArr: Array = [true, false];
console.log(boolArr);
5. 元组
let tuple: [string, number, boolean, string];
tuple = ['上海', 200, true, '北京'];
console.log(tuple);
let tuple1: [string, number, boolean, string] = ['上海', 200.232323, true, '北京'];
console.log(tuple1[0]);
console.log(tuple1[0].length);
console.log(tuple1[1].toFixed());
console.log(tuple1[2].valueOf());
console.log(tuple1[3].substr(1));
6. 枚举
自动赋值
enum Sex {
Man,
Women
}
let sex1: Sex = Sex.Women;
let sex2: Sex = Sex.Man;
console.log(sex1, sex2);
手动赋值
enum Sex {
Man = 2,
Women = 8
}
let sex1: Sex = Sex.Women;
let sex2: Sex = Sex.Man;
console.log(sex1, sex2);
通过枚举的值得到名字
enum Sex {
Man = 2,
Women = 8
}
let sexName: string = Sex[2];
console.log(sexName);
7. any
let str: any;
str = '我是小撩宝宝';
str = 100;
str = true;
let arr: any[] = ['张三', 19, true, '男'];
arr[3] = 2;
console.log(arr);
8. void
// let str: void = 10; //报错
let str1: void = null;
let str2: void = undefined;
console.log(str1, str2);
function logMsg(): void {
console.log('it like, like it');
}
logMsg();
9. null和undefined
let str1: null = null;
let str2: undefined = undefined;
let str3: null = undefined;
let str4: undefined = null;
let str5: string = null;
let str6: string = undefined;
console.log(str1, str2, str3, str4, str5, str6);
10. never
function error(msg:string): never{
throw new Error(msg);
}
// error('发生未知错误');
// 必须存在无法到达的终点
function func():never {
while(true){
console.log(1);
}
}
func();
11. object
object的一般使用
let obj1:object = {name: '小撩', age: 18};
console.log(obj1);
console.log(obj1.toLocaleString);
let obj = [1, 2, 3];
console.log(obj);
规定declare函数的参数必须是object
declare function func(o: object): void
func({name: '小撩'}) //OK
func([1,2,3]) //OK
func(null) //OK
func(undefined) //OK
func(123); //报错
func('小撩'); //报错
func(true); //报错
12. 类型断言
方式一:<>判断该object是string类型
let obj: any = 'like it, it like';
// let str: string = obj.substr(0, 3);
// console.log(str);
let str2: string = (obj).substr(0, 3);
let str3: string = (obj).toFixed(2);
console.log(str2);
console.log(str3);
方式二:as判断该object是string类型
let obj: any = 'like it, it like';
let str4: string = (obj as string).substr(0, 4);
console.log(str4);
声明和解构
1. var和let
// 1. var和let
// var存在的几个问题
// ①:仅在函数中才是块级作用域
// ②:变量提升
// 因此现在建议多多使用let
var str:string = '撩课';
let str1:string = 'itlike';
// 块级作用域
function func(flag: boolean): number{
let a = 99;
if(flag){
// let b = a + 1;
var b = a + 1;
console.log(b);
return b;
}
console.log(b);
return b;
}
func(false);
// 注意
function funcA(x){
let x = 100; //不OK,重复声明同一个变量
var x = 100; //不OK,重复声明同一个变量
}
function funcB(flag: boolean, x:number): void {
if(flag){
let x = 100; //OK,因为let是块级作用域,在if里面
}
}
2. const
const CAT_NAME: string = "喵喵";
// CAT_NAME = "哈哈哈"; //错误
const CAT = {
name: CAT_NAME,
age: 8
}
console.log(CAT);
// 错误
// CAT = {
// name: '小可爱',
// age: 1
// }
CAT.name = "小黑黑";
console.log(CAT);
3. 解构
数组的解构
let arr:number[] = [1,2];
let [one, two] = arr;
console.log(one, two);
// 交换两个变量的数值,但是这种方式不严谨,尽量少用
[one, two] = [two, one];
console.log(one, two);
// ...符号
let [first, ...reset] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(reset); // [2, 3, 4, 5]
对象的解构
enum Sex {
Man,
Women
}
interface Person {
personName: string,
personAge: number,
personSex: Sex
}
let person: Person = {
personName: '小撩',
personAge: 19,
personSex: Sex.Women
}
let {personName, personAge, personSex} = person;
console.log(personName, personAge, personSex);
接口
1. 接口的定义和使用
interface Person {
pName: string,
pAge: number,
pJob: string
}
// 初始化person的时候,变量的顺序是无所谓的,但是名称和类型必须和接口保持一致
let person: Person = {
pName: '小撩',
pAge: 18,
pJob: '咨询小姐姐'
}
function printPerson(person){
console.log(`我是:${person.pName}`)
}
printPerson(person);
2. 接口-可选属性
好处:
-
- 对某些属性进行预定义
-
- 捕获错误
// 输出接口
interface Circle {
color: string, // 颜色
area: number //面积
}
// 输入接口
interface CircleConfig {
// 可选属性
color?: string,
radius?: number
}
function createCircle(config: CircleConfig): Circle{
let newCircle = {color: 'green', area: 100};
if(config.color){
newCircle.color = config.color;
}
if(config.radius){
newCircle.area = Math.PI * config.radius * config.radius;
}
return newCircle;
}
let myCircle1 = createCircle({radius: 100});
console.log(myCircle1);
let myCircle2 = createCircle({color: 'red'});
console.log(myCircle2);
3. 只读属性
interface FullName {
readonly firstName: string,
readonly lastName: string
}
let p: FullName = {firstName: '张', lastName: '三丰'};
console.log(p);
console.log(p.firstName, p.lastName);
p.firstName = '李'; // 只读接口只能进行一次赋值,第二次就会报错
console.log(p);
console.log(p.firstName, p.lastName);
只读数组
// TS ReadonlyArray Array
let arr:number[] = [1,2,3,4];
arr.push(10);
// arr.pop();
console.log(arr);
let ra: ReadonlyArray = arr;
// ra.push(5); //error
// ra[0] = 10; //error
// ra.length = 1000; //error
console.log(ra);
// 重新将ra赋值给arr,可以使用断言
arr = ra as number[];
console.log(arr);
4. 额外的类型检查
4.1 使用断言
// 输出接口
interface Circle {
color: string, // 颜色
area: number //面积
}
// 输入接口
interface CircleConfig {
// 可选属性
color?: string,
radius?: number
}
function createCircle(config: CircleConfig): Circle{
let newCircle = {color: 'green', area: 100};
if(config.color){
newCircle.color = config.color;
}
if(config.radius){
newCircle.area = Math.PI * config.radius * config.radius;
}
return newCircle;
}
// 这种方式是无法给添加一个接口中没有的属性的
// let myCircle1 = createCircle({color: 'red', radiussss: 100});
// 1. 使用类型断言
let myCircle1 = createCircle({color: 'red',radius: 11, radiussss: 100} as CircleConfig);
console.log(myCircle1);
4.2 通过字符串的索引签名(推荐)
// 输出接口
interface Circle {
color: string, // 颜色
area: number //面积
}
// 输入接口
interface CircleConfig {
// 可选属性
color?: string,
radius?: number,
// 字符串的索引签名
[propsName: string]: any
}
function createCircle(config: CircleConfig): Circle{
let newCircle = {color: 'green', area: 100};
if(config.color){
newCircle.color = config.color;
}
if(config.radius){
newCircle.area = Math.PI * config.radius * config.radius;
}
return newCircle;
}
let myCircle1 = createCircle({color: 'red',radius: 11, radiussss: 100, a: 'q', c: 10});
4.3 对象赋值
// 输出接口
interface Circle {
color: string, // 颜色
area: number //面积
}
// 输入接口
interface CircleConfig {
// 可选属性
color?: string,
radius?: number,
}
function createCircle(config: CircleConfig): Circle{
let newCircle = {color: 'green', area: 100};
if(config.color){
newCircle.color = config.color;
}
if(config.radius){
newCircle.area = Math.PI * config.radius * config.radius;
}
return newCircle;
}
let circleOption = {color: 'red',radius: 11, radiussss: 100, a: 'q', c: 10};
let myCircle1 = createCircle(circleOption);
5. 函数类型
interface CompareFunc {
(first: number, last: number): boolean
}
// let myCompare: CompareFunc = function(first: number, last: number): boolean {
// return first > last;
// }
// let myCompare: CompareFunc = function(a: number, b: number): boolean {
// return a > b;
// }
let myCompare: CompareFunc = function(a, b) {
return a > b;
}
console.log(myCompare(10,20));
6. 可索引类型
interface StrArr {
[index: number]: string
}
let myArr:StrArr = ['it', 'like'];
let str:String = myArr[1];
console.log(str);
7. 类类型
7.1 属性
interface ClockInterface{
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number){
console.log(h, m);
}
}
7.2 描述一个方法
interface ClockInterface{
currentTime: Date;
setTime(d: Date)
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number){
console.log(h, m);
}
setTime(d: Date){
console.log(d);
}
}
8. 类的静态部分 和 实例部分
8.1 静态部分的类型
interface ClockConstructor {
new {h: number, m: number}
}
class Clock implements ClockConstructor {
constructor(h: number, m: number){
}
}
8.2 实例类型
9. 接口继承
interface Animal {
// 品种
breed: string
}
interface Cat extends Animal {
// 颜色
color: string
}
let cat = {} as Cat;
cat.breed = '蓝猫';
cat.color = '白色';
console.log(cat);
9.1 一个接口继承多个接口
interface Animal {
// 品种
breed: string
}
interface Mammal {
// 腿的数量
leg: number
}
interface Cat extends Animal,Mammal {
// 颜色
color: string
}
let cat = {} as Cat;
cat.breed = '蓝猫';
cat.leg = 4;
cat.color = '白色';
console.log(cat);
类
1. 基本使用
var Cat = /** @class */ (function () {
function Cat(catName) {
this.catName = catName;
}
Cat.prototype.say = function () {
return '大家好,我是: ' + this.catName;
};
return Cat;
}());
var cat = new Cat('小黑猫');
console.log(cat);
2. 继承
class Animal{
animalName: string;
constructor(animalName: string){
this.animalName = animalName;
}
logName(){
return '大家好,我是: ' + this.animalName;
}
}
class Dog extends Animal {
// 品种
breed: string;
constructor(dName: string, breed: string){
super(dName);
this.breed = breed;
}
logBreed(){
return `我的品种是${this.breed}`
}
}
let dog = new Dog('小土豆', '土狗');
console.log(dog);
console.log(dog.animalName, dog.breed);
console.log(dog.logName());
console.log(dog.logBreed());
3. 子类重写父类中的方法
class Animal{
name: string;
constructor(name: string){
this.name = name;
}
// 走动
move(distance: number = 0){
console.log(`${this.name}走动了${distance}m`)
}
}
class Snake extends Animal {
constructor(name: string){
super(name);
}
// 走动
move(distance: number = 10){
console.log(`我是爬行的......`);
// 调用父类的方法
super.move(distance);
}
}
class Horse extends Animal {
constructor(name: string){
super(name);
}
// 走动
move(distance: number = 500){
console.log(`我是飞奔的......`);
// 调用父类的方法
super.move(distance);
}
}
let snake:Snake = new Snake('小青蛇');
let horse:Animal = new Horse('白龙马');
snake.move();
horse.move(70);
4. 公共、私有、受保护、只读《修饰符》
4.1 公共的 public
// TS成员默认都是public
class Animal{
public name: string;
public constructor(name: string){
this.name = name;
}
public move(distance: number = 0){
console.log(`${this.name}走动了${distance}m`);
}
}
4.2 私有的 private
class Animal{
private name: string;
constructor(name: string){
this.name = name;
}
move(distance: number = 0){
console.log(`${this.name}走动了${distance}m`);
}
}
let cat = new Animal('小花花');
console.log(cat);
cat.name = "小喵喵"; // 会报错,name属性是私有的,在实例对象中也无法使用
cat.move(100);
在TypeScript中,所有的类都是结构性的
class Animal{
private name: string;
constructor(name: string){
this.name = name;
}
}
class Cat extends Animal{
constructor(name: string){
super('Cat');
}
}
class Dog{
private name: string;
constructor(name: string){
this.name = name;
}
}
// 实例
let animal = new Animal('猪猪');
let cat = new Cat('小喵喵');
let dog = new dog('dog');
animal = cat;
animal = dog;
cat = dog;
4.3 受保护的 protected
class Person {
protected name: string;
constructor(name:string){
this.name = name;
}
}
class Employee extends Person{
// 公司
private company: string;
constructor(name: string, company: string){
super(name);
this.company = company;
}
logMsg(){
return `我叫${this.name}, 我在${this.company}工作`
}
}
let p = new Employee('科比', 'NBA');
console.log(p.logMsg());
4.4 readonly修饰符
// 1) 可以使用'readonly'关键字将属性设置为只读的
// 2) 只读属性必须在声明时或构造函数里被初始化
class Person {
readonly name: string;
constructor(name: string){
this.name = name;
}
}
let person = new Person('小撩');
console.log(person.name);
person.name = '大撩'; // 会报错的,readonly修饰的属性只能赋值一次
5. 参数属性
// 参数属性可以方便地让我们在一个地方定义并初始化一个成员
// 1) 声明和赋值合并至一处
// 2) 参数属性通过给构造函数参数前面添加一个访问限定符来声明
class Person {
constructor(public name: string){}
}
let p = new Person('小撩');
p.name = '大撩';
console.log(p.name);
console.log(p);
6. 存取器
- TypeScript支持通过getter/setters来截取对对象成员的访问
- 可以有效地控制对对象成员的访问
注意:
1) >= ES5
2) 如果只实现了get, 默认就是readonly
需求:先检查密码是否正确,然后再允许修改员工的信息
// 密码:
let passCode = 'itLike.com';
class Employee {
private _fullName: string;
get fullName():string {
return this._fullName
}
set fullName(newName:string){
if(passCode && passCode === 'itLike.com'){
this._fullName = newName;
}else{
console.log("错误: 没有权限修改用户信息!")
}
}
}
let p = new Employee();
p.fullName = '科比';
console.log(p.fullName);
7. 静态属性
- 实例属性: 类的实例成员,仅当类被实例化的时候才会被初始化的属性
- 我们也可以创建类的静态成员,这些属性存在于类本身而不是类的实例上面
class Company {
// 静态属性
static title = '撩课';
// 实例属性
constructor(public college: string){};
// 输出
fullName(){
return Company.title + this.college;
}
}
let c1 = new Company('web学院');
console.log(c1.fullName());
let c2 = new Company('Java学院');
console.log(c2.fullName());
8. 抽象类
抽象类:
1) 抽象类作为其它派生基类使用。
2) 它们一般不会直接被实例化。
3) 不同于接口,抽象类可以包含成员的实现细节。
4) abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法
抽象方法:
1)抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
2) 抽象方法的语法与接口方法相似,两者都是定义方法签名但不包含方法体
3) 抽象方法必须包含abstract关键字并且可以包含访问修饰符
abstract class Department {
name: string;
constructor(name: string){
this.name = name;
}
printName():void{
console.log('部门名称: ' + this.name);
}
// 抽象方法
abstract printMetting():void // 必须在每一个继承的派生类中去实现
}
class AccountingDepartment extends Department {
constructor(){
super('财务部');
}
printMetting(): void {
console.log('财务部每天10:00开会');
}
payPage():void {
console.log('每天都发工资');
}
}
// 约束变量的类型
let department: Department;
// 下面这是错误的,抽象类不能直接实例化
// department = new Department();
department = new AccountingDepartment();
department.printName();
department.printMetting();
department.payPage(); // 错误: 方法的声明在抽象类中不存在
9. 把类当作接口使用
类定义会创建两个东西,类的实例类型和一个构造函数
因为类可以创建出类型,所以能够在允许使用接口的地方使用类
class Point {
x: number;
y: number;
}
interface Point3D extends Point{
z: number;
}
let point3D: Point3D = {x: 10, y: 20, z: 100};
函数
1. 基本示例
// 命名函数
function maxA(x:number, y:number):number{
return x > y ? x : y;
}
// 匿名函数
let maxB = function (x:number, y:number):number{
return x > y ? x : y;
}
// 箭头函数
let maxC = (x: number, y: number) => {
// this
}
let num1:number = 100;
function func(num2, num3):number {
return num1 + num2 + num3;
}
2. 可选参数
TypeScript 里的每个参数都是必须的
这不是指不能传递null
或undefined
作为参数,而是说编译器检查用户是否为每个参数都传入了值
不正确的操作
function max(x: number, y: number):number{
return x > y ? x : y;
}
let res1 = max(10);
let res2 = max(10, 20);
正确的操作
// 可选参数必须位于必选参数的后面
function max(x: number, y?: number):number{
if(y){
return x > y ? x : y;
}else {
return x;
}
}
let res1 = max(2);
let res2 = max(2, 4);
console.log(res1, res2);
// 可以预定义一些参数的值
function func(x: number, y = 4): void {
console.log(x, y);
}
func(12);
func(2, 1);
func(2, null);
3. 剩余参数
function sum(x:number, ...resetNumber: number[]):number{
let result:number = x;
for(let i = 0; i < resetNumber.length; i++){
result += resetNumber[i];
}
return result;
}
let result = sum(1,2,3,4,5,6);
console.log(result);
泛型
1. 初体验
function getNumber(num: number):number {
return num;
}
function getNumber(num: any):any {
return num;
}
getNumber(true);
2. 泛型变量
类型变量:它是一种特殊的变量,只用于表示类型而不是值
function getNumber(num: T):T {
return num;
}
let r1 = getNumber('一百万');
console.log(r1);
let r2 = getNumber(10);
console.log(r2);
// 调用的时候,不写泛型也是可以的,这个只是方便人看
let r3 = getNumber(true);
console.log(r3);
扩充
function getNumber(num: T):T {
// 因为如果是number类型就没有length方法,所以错误
console.log(num.length);
return num;
}
// 这样就可以啦
function getNumber(num: T[]):T[] {
console.log(num.length);
return num;
}
3. 泛型类
class Add {
zeroValue: T;
add: (x:T, y:T) => T;
}
// 3.1 number类型
let a = new Add();
a.zeroValue = 100;
a.add(10, 20);
// 3.2 其它类型
let a = new Add(){
a.zeroValue = '2';
a.add('200', '100');
}
4. 泛型约束
有时候我们想去C座某类型的一组值,并且我们知道这组值具有什么样的属性
这时,可以定义一个接口来描述约束条件
// 创建一个包含length属性的接口
// 通过继承这个接口,在函数调用的时候,参数必须具有length这个属性,object也可以哦
interface LengthWise {
length: number;
}
function getNum(num: T):T {
console.log(num.length);
return num;
}
console.log(getNum('10'));
console.log(getNum({value:10, length: 20}));
5. 在泛型约束中使用类型参数
function getProperty(obj: T, key: K){
return obj[key];
}
let person = {name: '小撩', age: 20, sex: '女'};
// 第二个参数,也就是key,只能是person的三个key
let p1 = getProperty(person, 'name');
let p2 = getProperty(person, 'age');
let p3 = getProperty(person, 'sex');
console.log(p1);
console.log(p2);
console.log(p3);
类型推断
1. TypeScript 里,在有些没有明确指出类型的地方,类型推断会帮助提供类型
let num = 10; // 数字
let str = '撩课'; // 字符串
2. 最佳通用类型
let arr = [0, 10, true, null] // (number | boolean | null)[]
// 如果是class的话,可能不是我们希望的类型
class Animal {
breed: string;
}
class Dog extends Animal{};
class Cat extends Animal{};
let zoo = [new Dog(), new Cat()]; // (Dog | Cat)[],而不是我们希望的Animal[]
// 这个时候就可以用强制类型
let zoo:Animal[] = [new Dog(), new Cat()];
3. 上下文类型
1)TypeScript类型推断会按另外一种方式,我们称作“上下文类型”
2)上下文类型的出现和表达式的类型以及所处的位置相关
window.onmousedown = function(mouseEvent){
console.log(mouseEvent.target); //OK
console.log(mouseEvent.liaoke); //不OK
}
上下文类型会在很多情况下使用到
- 通常包含函数的参数,赋值表达式的右边,类型断言,对象成员,数组字面量和返回值语句
- 上下文类型也会作为最佳通用的候选类型
class Animal {
breed: string;
}
class Dog extends Animal{};
class Cat extends Animal{};
// Animal > Dog > Cat
function createZoo(): Animal[] {
return [new Dog(), new Cat()];
}
高级特性
1. 联合类型:一个代码库希望传入多种类型的参数
/*
左侧拼接:
1)如果传入字符串,则直接拼接
2)如果传入数字,则创建空格拼接
3)其它的为非法
*/
// 1. 编译通过,运行报错
function padLeft(value: string, padding: any){
if(typeof padding === 'number'){
return Array(padding+1).join(' ') + value;
}
if(typeof padding === 'string'){
return padding + value;
}
throw new Error('出现错误');
}
console.log(padLeft('撩课学院', 10));
console.log(padLeft('撩课学院', '3343434343'));
console.log(padLeft('撩课学院', [21,32,334])); // 编译通过,运行报错
// 2. 编译不通过
function padLeft(value: string, padding: string | number){
if(typeof padding === 'number'){
return Array(padding+1).join(' ') + value;
}
if(typeof padding === 'string'){
return padding + value;
}
throw new Error('出现错误');
}
console.log(padLeft('撩课学院', 10));
console.log(padLeft('撩课学院', '3343434343'));
console.log(padLeft('撩课学院', [21,32,334])); // 编译不通过
2. 类型保护
联合类型适用于那些值可以为不同类型的情况
但当我们想确切地了解pet是否为Fish或者是Bird时,怎么办?
interface Bird{
fly();
sleep();
}
interface Fish{
swim();
sleep();
}
function getSmallPet(): Fish | Bird{
return
}
// 直接写当然是不行的
// let pet = getSmallPet();
// pet.sleep(); // 两种类型都有sleep方法
// pet.swim(); // Error,比如是Fish类型才可以
// 这样做,使用if else也是不行的
// let pet = getSmallPet();
// if(pet.swim){
// pet.swim();
// }else if(pet.fly){
// pet.fly();
// }
// 只能通过使用断言这种方式了
let pet = getSmallPet();
if(pet as Fish){
(pet as Fish).swim();
}else{
(pet as Bird).fly();
}
3. 自定义的类型保护
- 类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型
- 定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个类型谓词
interface Bird{
fly();
sleep();
}
interface Fish{
swim();
sleep();
}
function getSmallPet(): Fish | Bird{
return
}
// 谓词:p is type
function isFish(pet:Fish | Bird): pet is Fish{
return (pet as Fish).swim !== undefined;
}
let pet = getSmallPet();
if(isFish){
pet.swim();
}else{
pet.fly();
}
4. instanceof 类型保护
instanceof 类型保护是通过构造函数来细化类型的一种方式
class Bird{
fly(){
console.log('鸟在飞');
};
sleep(){
console.log('鸟在睡');
};
}
class Fish{
swim(){
console.log('鱼在游');
};
sleep(){
console.log('鱼在睡');
};
}
function getSmallPet(){
return Math.random() > 0.5 ? new Bird() : new Fish();
}
let pet = getSmallPet();
if(pet instanceof Bird){
pet.fly();
}
if(pet instanceof Fish){
pet.swim();
}
5. 可以为null的类型
- TypeScript具有两种特殊的类型, null和undefined,他们分别具有值null和undefined
- 默认情况下,类型检查器认为null与undefined可以赋值给任何类型
- 这就意味着:null和undefined是所有其它类型的一个有效值,这也意味着,你阻止不了
将它们赋值给其它类型,就算是你想要阻止这种情况也不行。null的发明者,Tony Hoare,称
它为价值亿万美金的错误
--strictNullChecks标记可以解决此错误:当你声明一个变量时,它不会自动包含null或undefined
let s = '撩课';
s = null; // 错误
console.log(s);
let s1:string | null = 'bar';
s1 = null;
console.log(s1);
s1 = undefined; // 错误
console.log(s1);