TS —— 1、定义、泛型、类、枚举、元组、抽象

目录

42、TypeScript

(1)JavaScript 与 TypeScript 的区别

(2)优势、缺点

(4)安装

(5)查看版本

(6)编译

(7)写法

(8)为什么要用到TS

 (9)定义

1.类型定义

(1)联合类型

 (2)任意类型

2.数组类型

(1)方式一:数组

(2)方式二:泛型

3.面向对象—接口类型

(1)接口的定义:

(2)可选属性:

(3)任意属性 :(一个接口允许随便加)

(4)泛型(常用)

 4.函数类型

(1)参数默认值

(2)可选参数

(3)接口在函数中的运用

(4)注意:length

(10)泛型

1.泛型类

2.什么时候需要定义泛型函数

3.泛型约束

(1)接口(常用)

 (2)数组

(3)类

(4)多个类型参数

(11)类型别名

(12)类

1.定义类

(1)ES5(构造函数)

(2)ES6

2.类的继承

3.修饰符

 4.类的类型

5.类实现接口

6.一个类可以实现多个接口

 (13)枚举

(14)元组

(15)抽象类abstract


42、TypeScript

TypeScript是.ts文件,与.js相似,但只是用来编写代码,运行还是在.js中(下面优势与缺点)

TypeScript 是一种由微软开发的自由开源的编程语言,他是JavaScript的一个超集,扩展了JavaScript的 语法,主要提供了类型系统和对 ES6 的支持。

TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

(1)JavaScript与 TypeScript 的区别

TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与

TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。

TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。

(2)优势、缺点

优点:

强大的IDE支持:体现在三个特性上:

1.类型检查,在TS中允许你为变量指定类型(js文件不会)

2.语法提示(js文件不会)

3.重构Angular2、vue3的开发语言

 //分别在两个文件中

缺点:

有一定的学习成本,需要理解

接口(Interfaces)、

泛型(Generics)、      

类(Classes)、

枚举类型(Enums)等前端开发可能不是很熟悉的知识点

(3)编辑器

TypeScript 最大的优势之一便是增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等。

主流的编辑器都支持 TypeScript,推荐使用 Visual Studio Code。

获取其他编辑器或 IDE 对 TypeScript 的支持:

Sublime Text

Atom

WebStorm

Vim

Emacs

Eclipse

Visual Studio 2015

Visual Studio 2013

(4)安装

npm install -g typescript

以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了

(5)查看版本

tsc -v

(6)编译

编译一个 TypeScript 文件

tsc hello.ts

使用 TypeScript 编写的文件以.ts为后缀

(7)写法

使用 :(冒号)指定变量的类型,:的前后有没有空格都可以

也就是说 :(冒号)后面的是类型

    let num:number = 15;
    num(变量名):number(类型) = 15(具体值)
    // 表示定义一个变量num,指定类型为number;
    let str:string = 'abc';
    // 表示定义一个变量str,指定类型为string;

 //.ts文件中这样写不会报错

 

  //自动生成一个同名的.js文件

 

(8)为什么要用到TS

    //定义一个函数计算二个数据的合计
    function sum(x, y) {
        if (typeof x != 'number') { //对于形参的类型要添加转换
            x = parseInt(x);
        }
        return x + y
    };
    sum('1', 2);	//3
    //TS的方式,直接约束了类型

 (9)定义

1.类型定义

    let flag: boolean = false; //布尔类型
    let num: number = 15; //数值类型
    let str: string = 'abc'; //字符串类型
    let str2: string = `hello,${str}`;
    let msg: string = `hello,${str},${num}`;
    let u: undefined = undefined;
    let n: null = null;

(1)联合类型

表示取值可以为多种类型中的一种

    let a4: string | number;	//a4为多个类型
    a4 = 'seven';
    a4 = 7;

 //提示错误(布尔类型)

    function getLength(str: string | number): number {	//第二个number是返回类型
        return str.length; //length提示错误,为什么?
    }
    console.log(getLength(123));
    //当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,只能访问此联合类型的所有类型里共有的属性或方法
===================================================================================
	//解决
    function getLength(str: string | number[]): number {
        return str.length;
    }
    console.log(getLength([1, 2, 3]));

 (2)任意类型

如果是一个普通类型,在赋值过程中改变类型是不被允许的,任意值(Any)用来表示允许赋值为任意类型

    let a1: string = 'seven';
    a1 = 7;	//提示错误
    //但如果是 any 类型,则允许被赋值为任意类型
    let a2: any = 'seven';
    a2 = 7;
-----------------------------------------------------------------------------------
    //变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
    let a3;
    a3 = 'seven';
    a3 = 7;
    //相当于
    let a3: any;
    a3 = 'seven';
    a3 = 7;

2.数组类型

(1)方式一:数组

在 TypeScript 中,数组类型有多种定义方式,比较灵活。

最简单的方法是使用 [类型+方括号]来表示数组:

    let arr: number[] = [1, 2, 3, 4, 5];
    // 数组的项中不允许出现其他的类型
    var arr2: number[] | string[] = [1, '2', 3, 4, 'a'];
    // 其它类型的数组
    let arr3: any[] = [1, '1', 2, 'x', {id:1},[1,2,3],true];

(2)方式二:泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

泛型变量<T> ,T可以表示任何类型

    let arr:Array<number> =[1,2,3];
    let arr:Array<number | string> =['1',2,3];		//定义多个类型

3.面向对象—接口类型

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象

接口(Interfaces)可以用于对「对象的形状(Shape)」进行描述。

(1)接口的定义:

    interface Person {
        name: string;
        age: number;
    }  
    let tom: Person = {
        name: 'Tom',
        age: 18
    };
    //上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person
    //这样,我们就约束了 tom 的形状必须和接口 Person 一致

定义的变量比接口多、少一个属性是不允许的:

    let tom2: Person = {	
        name: 'Tom'	
    };
    let tom3: Person = {	
        name: 'Tom',
        age: 25,
        sex: '男'		//提示错误
    };

(2)可选属性:

    interface Person2 {
        name: string;
        age?: number;	//可有可无
    }
    let tom4: Person2 = {	
        name: 'Tom'	//不会报错
    };
    // 可选属性的含义是该属性可以不存在。这时仍然不允许添加未定义的属性:
    let tom5: Person2 = {
        name: 'Tom',
        age: 25,
        sex: '男'		//提示错误
    };

(3)任意属性 :(一个接口允许随便加)

    interface Person3 {
        name: string;
        age?: number;
        [aa: string]: any;	//键名可以为任意类型
    };
    let tom6: Person3 = {
        name: 'Tom',
        //随便加
        sex: '男',
        fun: function(){ },
        o:{id: 1},
    };
    //一旦定义了任意属性,那么确定属性和可选属性都必须是它的子属性

(4)泛型(常用)

判断abc中是否包含a:

方式一:

 方式二:简化(推荐)

 

 方式三:

 4.函数类型

一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到

    //xy为输入的类型,:number为输出类型(number也可以写成void无返回类型)
    function sum1(x:number, y:number): number{ 
        return x+y;
    }

(1)参数默认值

    function sum3(x:number =5, y:number): number{
        return x+y;
    }
    let s1 = sum3(1, 2);

(2)可选参数

    function sum4(x: number, y?: number): number[]{	//?必须写在后面
        return [x, y]
    }
    let s2 = sum4(1);		//写一个会报错,?可以解决

(3)接口在函数中的运用

    //函数方式
    function f1(obj: {id: number, name: string}): number {
        return obj.id
    }
    var o = {id: 2, name: 'xyz'}
    f1(o)
-----------------------------------------------------------------------------------
    //接口方式
    interface ObjName{
        id: number
        name: string
    }
    function f2(obj: ObjName): number{
        return obj.id
    }

(4)注意:length

//length(长度)

解决:

===================================================================================

//length可以是任何类型

===================================================================================

    function f4<T>(arg: T): T {
        console.log(arg.length); 	//错误: T不存在length属性 比如数值等
    }							//length可以为任何类型

使用了 extends 约束了泛型 T 必须符合接口 LengthN 的形状,也就是必须包含 length 属性。

    interface LengthN {
        length: number;
    }
    function myHello<T extends LengthN>(arg: T): T {
        console.log(arg.length);
        return arg;
    }
    myHello <string>('123');
    myHello(123);	//错误(必须是字符串)
    myHello<number>(123);   //错误,只能写字符串类型(唯一写法?)
    myHello<boolean>(true); //错误

(10)泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定

类型的一种特性。

泛型变量<T> ,T可以表示任何类型

1.泛型类

    // 函数声明
    function mySum<T>(x: T): T {
        return x;
    }
    // 函数表达式
    let mySum1 = function <U>(x: U): U {	//U和T一样
        return x;
    };
    // ES6箭头函数
    let mySum2 = <T>(x:T):T => x;

2.什么时候需要定义泛型函数

要创建一个可重用的组件,其中的数据类型就必须要兼容很多的类型,那么如何兼容呢?

    function f(a: number, b: number): number[] {
        return [a, b]
    }
    f(1, 2);

    function f2(a: string, b: string): string[] {
        return [a, b]
    }
    f2('1', '3')

    function f2(a: boolean, b: boolean): boolean[] {
        return [a, b]
    }
//-----------------------------------------------------------------------------------
	// 为了解决上面返回不同类型的问题:
    // 泛型的定义
    function f3<T>(a:T, b:T): T[] {
        return [a, b]
    }
    f3<number>(1, 2)
    f3<string | number>(2, 'a')
    f3(1, 2)		//类型推断(不建议用,返回值不确定)
    f3('a','a')
    f3(1,'a')	//报错
    // 我们把f3这个函数叫做泛型,因为它适用于所有类型,并且不会有any类型存在的问题
    // 使用‘类型变量’ 一种特殊的变量,代表的是类型而非值

一旦我们定义了泛型函数,有两种方法可以使用。

第一种就是传入所有的参数,包括类型参数

    f3<number>(1, 2)
    f3<string | number>(2, 'a')

第二种是最常见的。我们使用/类型推断/ 编译器会根据传入的参数自动地帮助我们确定T的类型

f3(1, 2)		//类型推断(不报错,自动)不建议用,返回值不确定
f3('a','a')
f3(1,'a')	//报错

 //报错

 //如果返回类型是布尔,改一下返回类型即可

 

3.泛型约束

(1)接口(常用)

当你开始使用泛型,你可能会注意到当你创建一个类似"f4"的泛型函数,编译器会强制要求你在函数中正确的使用这些通用类型参数。

 

    function f4<T>(arg: T): T {
        console.log(arg.length); 	//错误: T不存在length属性 比如数值等
    }

使用了 extends 约束了泛型 T 必须符合接口 LengthN 的形状,也就是必须包含 length 属性。

    interface LengthN {
        length: number;
    }
    function myHello<T extends LengthN>(arg: T): T {
        console.log(arg.length);	//3(长度)
        return arg;
    }
    myHello<string>('123');	//"123"
    myHello(123);	//错误(必须是字符串)

=====================================================================

之前上面写过接口泛型,看一下:

方式一:

 方式二:简化(推荐)

 

 方式三:

 (2)数组

    let arrArray<number> = [11235];

这段代码编译不会报错,但是一个显而易见的缺陷是,返回值的类型不确定

案例:

    function f(length: number, value: any): Array<any> {
        let result = [];
        for (let i = 0; i < length; i++) {
            result[i] = value;
        }
        return result;
    }
    f(3, 'x');	//["x", "x", "x"]  3个x

在函数名后添加了,其中T用来指代任意输入的类型

简化:(更加灵活)

    function f<T>(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
            result[i] = value;
        }
        return result;
    }
    f<string>(3, 'x');	//["x", "x", "x"]  3个x

(3)类

    class A2<T>{
        n: T;
        constructor(num:  T) {
            this.n = num;
        }
        add(x: T): T {
            return x;
        }
    }
    var a2 = new A2 <number>(1);
    a2.add(3)

(4)多个类型参数

    function multi<N, S>(sum:[N,S]): [S,N] {
        return [sum[1], sum[0]];
    }
    multi <number, string>([3, 'one']);	//["one", 3]
===================================================================================
//案例:
//泛型约束
    interface Length3{
        length: number;
    }
    interface createA3<N, T extends Length3> {
        (a: N, b: T): Array<T>
    }

    var c3: createA3<number, string>;
    c3 = function <N, T>(i: N, v: T): Array<T> {
        var a: T[] = [];
        return a;
    };
    c3(2, '123');	

(11)类型别名

    function myHello(aa: n) {
        return 'Hello,' + aa;
    };

    type n = number;
    let name: n = 18;
    console.log(myHello(name));

(12)类

传统方法中,JavaScript 通过构造函数实现类的概念,通过原型链实现继承。

类(Class):定义了一件事物的抽象特点,包含它的属性和方法

1.定义类

(1)ES5(构造函数)

    // JavaScript 语言中,生成实例对象的传统方法是通过构造函数
    function Cat(name, color) { //构造函数
        this.name = name;
        this.color = color;
        //this.type='动物';
        //this.eat = function(){console.log("吃老鼠")};
    };

    // 原型中
    Cat.prototype.type = '动物';
    Cat.prototype.eat = function () {
        return console.log("吃老鼠");
    };
    var c1 = new Cat("大明", "黄色");
    var c2 = new Cat("小明", "白色");

(2)ES6

ES6 的类,完全可以看作构造函数的另一种写法。

注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以

另外,方法之间不需要逗号分隔,加了会报错。

    class Cat2 {
        name: string; 	//值类型
        color: string; 	//定义值类型
        constructor(name, color) {
            this.name = name;
            this.color = color;
        }
        eat() {
            return console.log("吃老鼠");
        }
        sayName() {
            return `My name is ${this.name}`;
        }
    }
    var c3 = new Cat2("小小明", "黑色");
    console.log(c3.eat())
    console.log(c3.sayName())
==================================== 编译后 ========================================
    var Cat2 = /** @class */ (function () {
        function Cat2(name, color) {
            this.name = name;
            this.color = color;
        }
        Cat2.prototype.eat = function () {
            return console.log('吃老鼠');
        };
        Cat2.prototype.aa = function () {
            return "you " + this.name;
        };
        return Cat2;
    }());
    var c3 = new Cat2("小小米", "黑色");
    console.log(c3.eat());
    console.log(c3.aa());

2.类的继承

    class Animal {
        name: string;
        constructor(name) {
            this.name = name;
        }
        eat() {
            return "吃骨头";
        }
    }
    //继承
    class Dog extends Animal {
        age: string;
        aa: string;
        constructor(name, age, aa) { 
            super(name)
            this.age = age;
            this.aa = aa;
        }
        sayHi() {
            return this.name + ',' + this.eat(); //调用父类的
        }
    }

    let d = new Dog('a', 'a', 'a');
    console.log(d.sayHi());

3.修饰符

public      属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的;

protected    属性或方法是受保护的,它和 private类似,区别是它能在子类中允许被访问的;

private     属性或方法是私有的,不能在声明它的类的外部访问;

protected:

	//常见写法
    class Obj3 {
        private name;
        public constructor(name) {
            this.name = name;
        }
        protected action() {
            return this.name
        }
    };
    class Dog2 extends Obj3 {
        a: string;
        b: string;
        c: string;
        constructor(name, a, b, c) {
            super(name);
            this.a = a;
            this.b = b;
            this.c = c;
        }
        say() {
            //return this.name  //调用的父类
            return this.action()
        }
    };
    let obj3 = new Obj3('tom');
    //obj3.name;
    //obj3.action(); 
    let d2 = new Dog2('abc', 'a', 'b', 'c');

 //static不需要实例化,直接调用

 4.类的类型

    class Animal5 {
        name: string;
        constructor(name: string) {
            this.name = name;
        }
        sayHi(): string {
            return `My name is ${this.name}`;
        }
    }
    let s4: Animal5 = new Animal5('Jack');
    console.log(s4.sayHi());	
==================================== 编译后 ========================================
    var Animal5 = (function () {
        function Animal5(name) {
            this.name = name;
        }
        Animal5.prototype.sayHi = function () {
            return "My name is " + this.name;
        };
        return Animal5;
    }());
    var s4 = new Animal5('Jack');
    console.log(s4.sayHi());    //My name is Jack

5.类实现接口

    interface Obbj4 {
        name: string;		//子类接了接口,属性和方法都必须写
        action(): string;
    }
-----------------------------------------------------------------------------------
//写法一
    class dog2 implements Obbj4 {
        name: string;
        constructor(name) {
            this.name = name;
        }
        action() {
            return '111'
        }
    }
    let s6 = new dog2('Jack');
-----------------------------------------------------------------------------------

//写法二

//接口的约束(注意name后面的逗号)

实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,

有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),

用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。

举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,

我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,

就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它:

    interface Alarm {	// 报警器
        sing(); 		//一个抽象的空方法
    };
    class Door { };	// 定义门
    // 防盗门继承门实现报警器的功能
    class SecurityDoor extends Door implements Alarm {
        sing() {
            console.log('SecurityDoor sing');
        }
    };
    // 车实现报警器的功能
    class Car implements Alarm {
        sing() {
            console.log('Car sing');
        }
    };
    var d1 = new SecurityDoor();
    var car = new Car();

6.一个类可以实现多个接口

    interface Alarm2 {
        alert();
    }
    interface Light {
        lightOn();
        lightOff();
    }
-----------------------------------------------------------------------------------
    // 实现多个接口Car实现了Alarm和Light接口,既能报警,也能开关车灯
    class Car3 implements Alarm2, Light {
        alert() {
            console.log('Car alert');
        }
        lightOn() {
            console.log('Car light on');
        }
        lightOff() {
            console.log('Car light off');
        }
        aaa() {
            console.log('aaa');
        }
    }
-----------------------------------------------------------------------------------

//这种写法只能用一个接口

 (13)枚举

数组中获取数据,只能通过数组来获取,不能通过名称来获取

enum类型是对JavaScript标准数据类型的一个补充

    var arr = ['a', 'b', 'c'];
    arr[0];		//可以
    arr['a'] 	//错误 不能通过名称来获取,只能索引

    var obj = {a: 'a', b: 'b'}; //对象
    obj.a 		//'a'	可以
    obj['a'] 	//'a'	可以
定义:
    enum Color {Red, Green=5, Blue};
    let c = Color.Blue;    		//6
    let c2: Color = Color.Green;	//5(元素编号会随之变化)

 

let c3 = Color[0];      //Red
let c4 = Color[1];      //undefined

let c4: string = Color[0];	//Red
    let c5: string = Color[2];	//undefined
任意值
    enum Color6 {Red=1.6, Green, Blue=<any>"b"};
let c6: number = Color6.Red;	//1.6
let c6: string = Color6.Green;//2.6(元素编号会随之变化)
let c6: number = Color6.Blue;	//b

 如果未手动赋值的枚举项与手动赋值的重复了后面的会覆盖前面的,尽量不要重复

    enum Color7 {Red=1, Green=3, Blue=<any>"b"};
    let c7:string = Color7[1];  	//Red 
    let c8:string = Color7[3];	//Green 
    let c9:string = Color7['b'];	//Blue 

枚举项有两种类型:常数项和计算所得项

    var aa='123';
    enum Color8{Red, Green=<any>aa, Blue="blue".length};
    let c:Color8 = Color8.Green;	//123
    let c:Color8 = Color8.Blue;	//4

如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错

    var aa='123';
    enum Color9 {Red=<any>aa, Green=<any>aa, Blue="blue".length}; //可以的
    //enum Color9 {Red=<any>aa, Green, Blue}; //这种错误

(14)元组

数组合并了相同类型的对象

元组合并了不同类型的对象。

我们知道数组中元素的数据类型都一般是相同的(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组

    var lists: number[] = [1, 2, 3];
    var lists2: Array<string> = ['a', 'b', 'c'];	//数组泛型 不允许有其它的类型
    var lists3: Array<string | number> = ['a', 'b', 1, 2];
    //任意值
    var lists4: any[] = ['a', 1, true];
===================================== 元组 =========================================
    let x: [number,string];
    x = [5,'abc'];

元组它允许表示一个已知元素数量和类型的数组,各元素的类型不必相同

1、数组中的数据类型必须和规定的类型顺序对应起来

2、当使用越界索引给数组赋值的时候,会使用联合类型(只要值是规定类型的某一种即可)

    var ff = function () {
        let x1: [string, number];
        x1 = ['abc', 5];
        x1[1] = 10;
        console.log(x1); 	//["abc", 10]
    }
    ff();

(15)抽象类abstract

  1. 抽象类的子类必须实现抽象类里的抽象方式
  2. 不能被实例化
    abstract class A {
        abstract action():void;   
    }
    class B extends A {
        action(){	//1.子类必须实现此方法   
        }
    }
    var a = new A();	//2.抽象类不能实例化

版权声明:本文为weixin_45042569原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。