走进TypeScript

作者: 写前端的大叔 | 来源:发表于2020-02-02 22:29 被阅读0次

    如今前端技术层出不穷,es6还没完全掌握,TypeScript又来了,2020了,不会点TypeScript都不好意思说自己是做前端的了。虽然技术层出不穷,但万变不离其宗,根据多年的开发经验,学习新技术其实也不是那么难,大多都差不多的,趁放假有时间,来了解下TypeScript,等上班的时候就可以用上了。TypeScriptJavaScript 的一个超集,支持 ECMAScript 6 标准。其实跟swift差不多,下面就来整理TypeScript相关的知识点。

    1.数据类型

    TypeScript的数据类型跟JavaScript差不多,有数字,字符串,布尔值,数组等类型,还有结构体、元组、枚举类型等。

    1.1.基础数据类型

    定义数字,字符串,布尔值,数组等类型时,跟JavaScript差不多,只是定义的语法稍微有一点点区别。如下所示:

    let isLike: boolean  = false;
    let index: number = 1;
    let firstName: string = 'haha';
    let result: string = `my name is ${firstName}`;
    let list:Array<number> = [1,2,3]
    

    1.2.元组

    元组是一个已知元素数量和类型的数组,各类型的元素不必相同,当访问一个已知索引的元素,会得到正确的类型,当访问一个越界的元素时,不会报错,会使用联合类型替代。

    let array: [number, string] = [12, 'haha'];
    console.log(array[0]);//12
    console.log(array[1]);//haha
    array[3] = 'ha';
    console.log(array[3]);//ha
    

    1.3.枚举

    枚举类型是为一组数值赋予友好的名字。使用关键字enum进行定义,如下所示:

    enum Color { RED, GREEN, BLUE };
    let color: Color = Color.RED;
    console.log(color);//0
    

    默认情况下是从0 开始的,也可以手动设置成员的数值,如下所示:

    enum Color { RED = 1, GREEN, BLUE };
    let color: Color = Color.RED;
    console.log(color);//1
    

    除了获取其成员的数值外,也可以通过数值来访问枚举的名称,如下所示:

    enum Color { RED = 1, GREEN, BLUE };
    console.log(Color[1]);//RED
    

    1.4.Any

    Any代表任意类型的变量,它允许你在编译时可选择地包含或移除类型检查,比如将一个数组指定为Any时,可以往数组中添加任意类型的数据。

    let array: Array<any> = [1, 'haha', true, { a: 'aa' }];
    console.log(array)
    

    1.5.Void

    Void类型与Any相关,表示没有任何类型,比如定义函数时,没有返回时,默认的类型为Void类型。

    function test() :void {
        console.log('haha');
    }
    test();
    

    1.5.Null 和 Undefined

    TypeScript里,undefinednull两者各自有自己的类型分别叫做undefinednull。 和 void相似,它们的本身的类型用处不是很大。默认情况下nullundefined是所有类型的子类型。 就是说你可以把 nullundefined赋值给number类型的变量。

    1.6.Never

    never是表示那些永不存在的类型的值,一般用在抛出异常时,如下所示:

    // 返回never的函数必须存在无法达到的终点
    function error(message: string): never {
        throw new Error(message);
    }
    

    1.6.Object

    Object表示那些除基本数据外的类型,也就是除numberstringbooleansymbolnullundefined之外的类型。

    1.7.类型断言

    类型断言相当于类型转换,它没有运行时的影响,只是在编译阶段起作用。类型断言有两种方式,一种是会用<>,一种是使用as。如下所示:

    let str: any = 'this is string';
    let len: number = (<string>str).length;
    var lenght: number = (str as string).length;    
    console.log(len);//14
    console.log(lenght);//14
    

    1.8泛型

    泛型是一种创建可复用代码组件的工具,使用代码能提供代码的可复用性和通用性。用过泛型能有效增加类、类型和接口的使用能力。最常用的是在数组中限定类型,如下所示:

    class Person {
        age: number;
    }
    class Animal {
        name: string;
    }
    
    let array: Array<Person> = [];
    let person: Person = new Person();
    let animal: Animal = new Animal();
    array.push(person);
    array.push(animal);//error
    

    定义数组的时候,使用<>定义了一个泛型,只能往数组中添加Person的对象,如果往数组中添加Animal的对象将报错。
    泛型也是类型,泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样:

    function identity<T>(arg: T): T {
        return arg;
    }
    
    let myIdentity: <T>(arg: T) => T = identity;
    

    2.接口

    TypeScript的核心之一是对值所具有的类型进行检查,在TypeScript里,可以通过定义接口来设定一些类型规则。如下所示:

    interface Person { 
        name:string
    }
    
    function test(person: Person) { 
        console.log(person.name);//haha
    }
    
    var obj = { name: 'haha', age: 10 }
    test(obj);
    

    如上代码所示,test函数中有一个person参数,并且类型为Person,该参数做了限制,传给函数的参数必须要有 name属性,否责会报错。

    2.1可选属性

    上面的代码定义的 name属性是必须的,如果传给函数的参数没有name属性会报错,可以通过可选属性来解除这种限制,可选属性使用?来设置。如下所示:

    interface Person { 
        name?: string
        age?:number
    }
    
    function test(person: Person) { 
        console.log(person.name);//haha
    }
    
    test({age: 10 });
    

    2.2只读属性

    在定义变量的时候,可以使用readonly来限制外部修改属性值,在创建的时候赋值后不能再修改。如下所示:

    interface Person { 
        readonly name: string
        age:number
    }
    
    var obj = { name: 'haha', age: 10 }
    obj.name = 'hhhh'//error
    

    2.3函数类型

    接口除了描述带有普通属性的普通对象外,也可以描述函数类型。为了使用接口表示函数类型,需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。如下所示:

    interface PersonInfo { 
        (name:string,age:number):string
    }
    

    定义好函数类型后,就可以像使用其它接口一样使用这个函数类型的接口,如下所示:

    interface PersonInfo { 
        (name:string,age:number):string
    }
    
    let personInfo: PersonInfo;
    personInfo = function (name: string, age: number): string { 
        var result: string;
        result = `name:${name}
        age:${age}`
        return result;
    }
    
    console.log(personInfo('haha', 12));
    

    2.4可索引类型

    可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。如下所示:

    interface StringArray { 
        [index:number]:string
    }
    let array: StringArray = ['haha1', 'haha2']
    console.log(array[1]);//haha2
    

    TypeScript支持两种索引签名,分别为数字和字符串。但是数字索引返回的类型必须是字符串索引返回类型的子类。这是因为用数字是索引时,javascript会默认将数字转换为字符串后再去索引对象。

    class Animal {
        name: string;
    }
    class Dog extends Animal {
        breed: string;
    }
    
    // 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
    interface NotOkay {
        [x: number]: Animal;
        [x: string]: Dog;
    }
    

    2.5类类型

    TypeScript也能够使用接口来明确的强制一个类去符合某种契约。定义好接口后,使用implements来实现接口,然后在类中添加接口的属性和方法,如下所示:

    interface Person { 
        name: string,
        eat();
    }
    class Student implements Person { 
        name: string;
        eat() { 
            console.log(`${this.name}在吃东西`)
        }
    }
    
    var stu = new Student();
    stu.name = 'haha';
    stu.eat();//haha在吃东西
    

    2.6接口继承

    接口也是可以继承,并且可以多重继承,可以很方便的将一个接口里的成员复制到另一个接口中,如下所示:

    interface Shape {
        color: string;
    }
    
    interface PenStroke {
        penWidth: number;
    }
    
    interface Square extends Shape, PenStroke {
        sideLength: number;
    }
    
    let square = <Square>{};
    square.color = "blue";
    square.sideLength = 10;
    square.penWidth = 5.0;
    

    3.类

    TypeScript中的类跟ES6中的类差不多,只是定义属性的方式稍微有点差别,以下为 TypeScript中定义的类。

    class Animal { 
        name: string;
        constructor(name: string) { 
            this.name = name;
        }
        eat() { 
            console.log('动物会吃东西');
        }
    }
    
    class Dog extends Animal { 
        constructor(name: string) { 
            super(name);
        }
        eat() { 
            console.log(this.name +' 在吃东西');
        }
    }
    
    class Cat extends Animal { 
        constructor(name) { 
            super(name)
        }
        eat() { 
            console.log(this.name +' 在吃东西');
        }
    }
    
    let dog: Dog = new Dog('haha');
    dog.eat();
    let cat: Animal = new Cat('cat')
    cat.eat();
    

    .3.1变量修饰符

    TypeScript里可以使用public,private,protected,readonly修饰成员变量,默认为public

    • public:默认的修饰符,外部可以访问。
    • private:当成员被标记成 private时,它就不能在声明它的类的外部访问。
    • protectedprotected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。
      -readonlyreadonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。

    3.2存取器

    TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。

    class Animal { 
        name: string;
        private _fullName: string;
        constructor(name: string) { 
            this.name = name;
        }
        set fullName(value) { 
            console.log('set fullname')
            this._fullName = value;
        }
    
        get fullName() { 
            console.log('get fullname')
            return this._fullName;
        }
        eat() { 
            console.log('动物会吃东西');
        }
    }
    

    fullName添加了getset方法。
    3.3 static
    使用static可以定义静态属性和静态方法。

    class Animal { 
        static fullName: string;
        static eat() { 
            console.log(Animal.fullName)
        }
    }
    Animal.fullName = 'haha'
    Animal.eat();//haha
    

    4.类型推论

    TypeScript里,在使用变量时可以推断出变量属性什么类型。

    4.1自动推论:

    在对属性和成员进行初始化时,可以进行自动推论,如下所示,将推断出变量x的类型为number类型。

    let x = 12;
    

    4.2最佳通用类型

    当需要从几个表达式中推断类型的时候,会使用这些表达式的类型来推断出一个最合适的通用类型。如:

    let x = [0, 1, null];
    

    如果没有找到最佳通用类型,类型推断的结果为联合数组类型。

    5.类型兼容性

    TypeScript里的类型兼容性是基于结构子类型的。 结构类型是一种只使用其成员来描述类型的方式。 它正好与名义(nominal)类型形成对比。比如下面的两个类,默认被当成同一个类型。

    class Person {
        name: string;
    }
    class Animal {
        name: string;
    }
    

    这是因为在基于名义类型的类型系统中,数据类型的兼容性或等价性是通过明确的声明和/或类型的名称来决定的。这与结构性类型系统不同,它是基于类型的组成结构,且不要求明确地声明。

    6.高级类型

    6.1交叉类型

    交叉类型是将多个类型合并成一个类型,它包含了所有类型的特性。如下所示:

    class Person {
        name: string;
    }
    class Animal {
        name: string;
        age: number;
        run() {
            console.log('running')
         }
    }
    function extend<T, U>(first: T, second: U): T & U {
        let result = <T & U>{};
        for (let key in first) {
            (<any>result)[key] = (<any>first)[key]
        }
    
        for (let key in second) { 
            (<any>result)[key] = (<any>second)[key];
        }
        return result;
    
    }
    
    var obj = extend(new Person(), new Animal());
    obj.run();
    

    6.2联合类型

    联合类型表示一个值可以是几种类型之一,使用|分隔每个类型。所以 number | string | boolean表示一个值可以是 numberstring,或 boolean。如果一个值是联合类型,我们只能访问联合类型中所有类型的共有成员,如下所示:

    class Person {
        name: string;
        eat() {
            console.log('Person eat')
        }
        
    }
    class Animal {
        name: string;
        age: number;
        run() {
            console.log('running')
         }
        eat() {
            console.log('Animal eat')
         }
    }
    
    function getObj(): Person | Animal{ 
        return new Animal();
    }
    let obj1 = getObj();
    obj1.eat();
    obj1.run();//error
    

    个人博客

    相关文章

      网友评论

        本文标题:走进TypeScript

        本文链接:https://www.haomeiwen.com/subject/awkxxhtx.html