TypeScriptz学习笔记

2020-08-09  本文已影响0人  爱吃胡萝卜的小白兔

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<boolean> = [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 = (<string>obj).substr(0, 3);
let str3: string = (<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<T> Array<T>
let arr:number[] = [1,2,3,4];
arr.push(10);
// arr.pop();
console.log(arr);

let ra: ReadonlyArray<number> = 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. 存取器

  1. TypeScript支持通过getter/setters来截取对对象成员的访问
  2. 可以有效地控制对对象成员的访问
注意:
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. 静态属性

  1. 实例属性: 类的实例成员,仅当类被实例化的时候才会被初始化的属性
  2. 我们也可以创建类的静态成员,这些属性存在于类本身而不是类的实例上面
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 里的每个参数都是必须的
这不是指不能传递nullundefined作为参数,而是说编译器检查用户是否为每个参数都传入了值

不正确的操作

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<T>(num: T):T {
    return num;
}

let r1 = getNumber<string>('一百万');
console.log(r1);
let r2 = getNumber<number>(10);
console.log(r2);
// 调用的时候,不写泛型也是可以的,这个只是方便人看
let r3 = getNumber(true);
console.log(r3);

扩充

function getNumber<T>(num: T):T {
    // 因为如果是number类型就没有length方法,所以错误
    console.log(num.length);
    return num;
}

// 这样就可以啦
function getNumber<T>(num: T[]):T[] {
    console.log(num.length);
    return num;
}

3. 泛型类

class Add<T> {
    zeroValue: T;
    add: (x:T, y:T) => T;
}

// 3.1 number类型
let a = new Add<number>();
a.zeroValue = 100;
a.add(10, 20);

// 3.2 其它类型
let a = new Add<string>(){
    a.zeroValue = '2';
    a.add('200', '100');
}

4. 泛型约束

有时候我们想去C座某类型的一组值,并且我们知道这组值具有什么样的属性
这时,可以定义一个接口来描述约束条件

// 创建一个包含length属性的接口
// 通过继承这个接口,在函数调用的时候,参数必须具有length这个属性,object也可以哦
interface LengthWise {
    length: number;
}

function getNum<T extends LengthWise>(num: T):T {
    console.log(num.length);
    return num;
}

console.log(getNum('10'));
console.log(getNum({value:10, length: 20}));

5. 在泛型约束中使用类型参数

function getProperty<T, K extends keyof T>(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
}

上下文类型会在很多情况下使用到

  1. 通常包含函数的参数,赋值表达式的右边,类型断言,对象成员,数组字面量和返回值语句
  2. 上下文类型也会作为最佳通用的候选类型
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. 自定义的类型保护

  1. 类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型
  2. 定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个类型谓词
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的类型

  1. TypeScript具有两种特殊的类型, null和undefined,他们分别具有值null和undefined
  2. 默认情况下,类型检查器认为null与undefined可以赋值给任何类型
  3. 这就意味着: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);
上一篇下一篇

猜你喜欢

热点阅读