TypeScript基础

350 阅读25分钟

一、ts介绍与开发

1、介绍

1)TypeScript是由微软开发的一款开源的编程语言。

2)TypeScript是Javascript的超集,遵循最新的ES6/ES5规范。TypeScript扩展了Javascript的语法。

3)TypeScript更像后端Java、C#这样的面向对象的语言,可以让js开发大型项目。

4)谷歌也在大力支持TypeScript的推广,谷歌的angular2.x+就是基于TS语法。

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

6)TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。

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

2、安装

注:在全局安装

cnpm  i  typescript -g

3、运行

// ts基本数据类型
let str:string = "你好!";

运行上面的代码:

tsc 文件名.ts(他会自动生成一个相同文件名的js文件)

注:也可以指定文件编译到那块去

tsc ./src/index.ts --outFile ./dist/index.js

上面这个表示在与src同级的地方创建一个dist文件,并把编译出来的文件命名为index.js。

4、实时编译

1)在文件夹下运行(可以在同级的地方,也可以在上一级),但是要删除之前的编译文件

tsc --init

注:它会出现一个tsconfig.json文件,但是 我们要对他进行修;(这样的话,就会多一个dist文件夹) 在这里插入图片描述

2)运行配置文件,让它进行监听(在vsc中点击终端——>运行任务——>tsc:监听);也可以用指令监听(具体的命令,自己查文档)

这样就可以实时监听文件了。 在这里插入图片描述

二、类型的介绍

1、字符串

​ TypeScript像其它语言里一样,使用string表示文本数据类型。 和JavaScript一样,可以使用双引号(")或单引号(')表示字符串。

let str:string = "你好!!!";
str = "你好!世界。";

​ 也使用模版字符串,定义多行文本和内嵌表达式。 这种字符串是被反引号包围 ` ,并且以${ expr }这种形式嵌入表达式。

let num:number = 9;
let str = `我的年龄${num}`;
console.log(str);

注:如果要查看结果的话,要把编译出来的那个js文件引入html中去。

2、boolean 类型(true false)

​ 最基本的数据类型就是简单的true/false值,在JavaScript和TypeScript里叫做boolean

let bool:boolean = true;

3、数字类型

​和JavaScript一样,TypeScript里的所有数字都是浮点数。 这些浮点数的类型是number

let num:number = 9;

数字类型和JavaScript一样,TypeScript里的所有数字都是浮点数。 这些浮点数的类型是number

let num:number = 9;

4、数组

TypeScript像JavaScript一样可以操作数组元素。 有两种方式可以定义数组。 第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:

let num:number[] = [1,2,4,5];//当然这个里面也可以写字符串,但是  如果你写字符串的话,那个number就要换成string
console.log(num);

第二种方式是使用数组泛型,Array<元素类型>:

let arr:Array<number> = [1,2,3,4];
console.log(arr);

5、元组

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 stringnumber类型的元组。

// 声明一个元组类型 x
let x: [string, number]
// 初始化 x
x = ['hello', 10] // OK
// 无效的初始值(这个会报错)
x = [10, 'hello'] // Error

当访问一个已知索引的元素,会得到正确的类型:

// 声明一个元组类型 x
let x: [string, number]
// 初始化 x
x = ['hello', 10] // OK
console.log(x[0].substr(1));//ello
/* substr是方法,他只能取到字符串 */

当访问一个越界的元素,会出现错误,既是根据下标去取一个大于应有长度的时候,他就会报错。

6、枚举

注:可以在周几的时候用、请求成功失败等地方可以使用。 1)enum(枚举)类型是对JavaScript标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字。(既可以为一组数据友好的赋值名字)

enum Color{//枚举里面一般都是大写,或者首字母大写
    RED=3,WHITE,BLUE
}
//下面表示c是枚举类型
let c:Color = Color.WHITE;
console.log(c);//这个打印出来是下标
/*表示枚举是从下标为零开始的(当然也可以指定下标,既就像上面的那样,那么BLUE 的下标就是 5了)
  也可以全部手动赋值,那这样的话,下标就没有意义了
*/

2)枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为2,但是不确定它映射到Color里的哪个名字,我们可以查找相应的名字。 既代表根据下标找到对应的元素

enum Color{//枚举里面一般都是大写,或者首字母大写
    RED=3,WHITE,BLUE
}
//下面表示c是枚举类型
let c:Color = Color.WHITE;
console.log(c);//这个打印出来是下标 4

let title:string = Color[c];
console.log(title); //WHITE

7、any

1)在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。

let notSure:any = 4
notSure = "maybe a string instead" // 赋值了一个字符串
notSure = false // 赋值了一个布尔值
console.log(notSure);//false

2)在对现有代码进行改写的时候,any 类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。

let notSure:any = 4
notSure.toString();//任意类型你赋值的是数字,但是调用的时候,你可以用字符串的方法

3)当你只知道一部分数据的类型时,any 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据(既一个数组里面有数字和字符串)

let list: any[] = [1, true, "free"];
//这样写它不会报错,当你知道它里面有什么类型的时候,南无这块的这个数组,你就可以用元组去定义

8、void

某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void(既表示他是没有返回值的)

function echo(): void {
    console.log('啊啊啊啊啊啊');//啊啊啊啊啊啊
}
//下面的是调用上面的函数
echo();


function echo(): void {
    return "啊啊啊啊啊啊啊啊";//这样他会报错,但是你把void改成string,他就可以了。
}
echo();

注:当一个函数没有任何返回值的时候,我们就可以把这个函数用 :void 来进行描述,用它来进行描述的时候,代表它 没有任何返回值。(void一般都用在函数上)

三、类型推断

如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。

1、什么是类型推断

当代码没有指定类型的时候,在编译的时候会出现报错。

let str = "啊啊啊啊啊";//内部会根据等号右边的自己进行判断,推断出为string类型的
str = 9;//在这块你后面给他赋值为数字的时候,就会报Type '9' is not assignable to type 'string'.

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。 如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:

let string;
string = 7;
string = "你好";
console.log(string);// 你好
//当上面 只定义不赋值,你下面可以任意赋值

2、联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。

let str:string | number | Array<number>;
//这块表示str既可以支持字符串也可以支持数字,也可以事数组,但是这块表示数组里面要是数字
str = "你好!世界";
console.log(str);

注:联合类型用 | 分隔。

3、访问联合类型的属性或方法

// 下面表示传进函数的类型,并且确定一个返回值的类型
function means(str:string) : number{
    return str.length
}

console.log(means("hello"));// 5

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法。

// 下面表示传进函数的类型,并且确定一个返回值的类型
 //下面这块也可以事联合类型,让其可以传字符串和数字(但是返回值智能放,两个同时支持的方法)
function means(str:string | number) : string{
    return str.toString();//这块事把传进来的 都改成字符串
}

console.log(means("hello"));// hello

访问了number和string的公共属性toString,是没有任何问题的!

4、联合类型赋值的类型推断

联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型。

let str : string | number;
str = "hello";
console.log(str.length);//5
str = 9;
console.log(str.length);
//这块会报错,因为上面赋值的的是数字,就会推断成数字类型,数字类型 没有长度

在这里插入图片描述

5、Null 和 Undefined

null 是一个只有一个值的特殊类型。表示一个空对象引用。用 typeof 检测 null 返回是 object。 typeof 一个没有值的变量会返回 undefined。 null 和 Undefined 是其他任何类型(包括 void)的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 null 或 undefined。 注:关闭严格模式: 在tsconfig.json里面进行关闭(将"strict": true, 改为false) 在这里插入图片描述

/* 
    null和undefined是任何类型的子类型,就代表着在非严格模式下,就可以给任何类型赋值操作
    注:严格模式默认是开启的,需要自己去
    tsconfig.json里面进行关闭(将"strict": true, 改为false)。
*/
let x : number = 1;
x = undefined;//这块会报错(但是关闭严格模式后,他就不会报错了)

注:在严格模式中,可以使用联合模式,那样他也就 不会报错。

在这里插入图片描述

6、Never类型

never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。

never类型是任何类型的子类型,代表从不会出现的值,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。 下面是一些返回never类型的函数:

/**
 * never 永远不
 * never类型表示的是 那些永不存在的值的类型
 * 一般就是说函数内部抛出异常 or 函数内部有死循环,一直返回不到结果的时候,无休止的执行内部逻辑
 */

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {//永远 没有结果的值
    throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
        
    }
}

7、Symbol(他是基本数据类型)

       自ECMAScript 2015起(既就是ES6),symbol成为了一种新的原生类型,就像number和string一样。 symbol类型的值是通过Symbol构造函数创建的。

//要使用Symbol,要去配置文件中,把es5改成es6,这样 就不会报错了
let syml = Symbol();
let sym2 = Symbol();

console.log(syml === sym2);//false   
//在浏览器中返回false,说明这两个对象是不同的,因为Symbol是独一无二的,即使 你传相同的参数


//使用
//Symbol可以用来作为对象的键  来使用
const sym = Symbol();//他是变量(只能用const,不能用let)
let obj = {
    [sym]:"哈哈",//这是一个键值对
    "name":"用户名字:小明"
}
console.log(obj[sym]);//哈哈
console.log(JSON.stringify(obj));//{"name":"用户名字:小明"}  只能拿到它

//通过上面的打印,发现Symbol当作对象的key(键)的时候,是不能被JSON.stringify() 和 for...in...进行遍历的
//使用一:利用这个特点  更好的去设计对象,使   对内操作和对外选择输出 变得更加优雅。

console.log(Object.getOwnPropertyNames(obj));//["name"]  获取不到

console.log(Object.getOwnPropertySymbols(obj));//这个 可以拿到这个对象

//使用es6的新增反射api
console.log(Reflect.ownKeys(obj)); //(2) ["name", Symbol()]



// 使用二:使用Symbol来替代常量来命名(Symbol是独一无二的)
const RED = Symbol();
const GREEN = Symbol();
const BLUE = Symbol();


let a:any;
a = RED;//
switch(a){
    case RED: //在这匹配a是不是红色,是红色在执行后续的 业务逻辑
        break;
}

//Symbols也可以与计算出的属性名声明相结合来声明对象的属性和类成员。
const getClassNameSymbol = Symbol()

class C {
    // 使用Symbol当成类的成员方法
    [getClassNameSymbol](){
        return "C"
    }
}

let c = new C()
let className = c[getClassNameSymbol]() 
console.log(className)   //"C"

注:下面是以此打印的截图: 在这里插入图片描述

四、函数

1、函数类型

1)函数声明的方式

function sum(x:number,y:number):number{//最后这个number是返回的类型
    return x + y;
}
console.log(sum(2,1));//3

注:一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单;输入多余的(或者少于要求的)参数,是不被允许的

2)函数表达式的方式

//下面这种定义方式只对等号右边进行了约束,然后根据等号右边是个函数然后内部经过自动推断,所以编译通过
let num = function(x:number,y:number):number{//这块应该要给num加上类型,没有报错是因为类型推断   
    return x + y;
} 
console.log(num(1,1));//2



// 下面是严谨的写法(平常使用它),调用方法同上
//在ts中箭头函数代表  我要返回的是什么数据类型
let digit:(x:number,y:number) => number = function(x:number,y:number):number{
    return x * y;
}

注:在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

2、用接口定义函数的形状

注:用接口的方式来定义一个函数需要符合的形状。

interface Animal{//interface它就是接口,接口名称  一般大写
    //定义了一个函数的基本形状  带一个字符串类型的参数,并且返回值是字符串类型
    (food:string):string
}

let dog:Animal; //定义了一个dog变量,然后是接口Animal类型的
dog = function(food:string):string{//这块是赋值操作,并且定义了类型
    return food;
}
//这里需要传递一个参数
console.log(dog("狗粮"));//狗粮

3、可选参数

注:用 ?表示可选的参数

// 我们通过 ? 的方式去定义可选参数,可以定义 也可以不定义
function buildName(firstName: string, lastName?: string) {//注:这个可选的  一定要在后面
    if (lastName) {
        return firstName + ' ' + lastName;//这块是两个拼接
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
console.log(tomcat);//Tom   Cat
let tom = buildName('Tom');
console.log(tom);//Tom

注:可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了

4、参数默认值

在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数。

//参数默认值,可以放到前面,可以放到任何位置,但是必须要传一个,如果实在不想传,就用undefined占位
function text(onetext:string = "老虎",twotext:string){
    // 这块给判断  表示怎末拼接
    if(twotext){//这块表示有twotext的值的时候,走这里,没有的时候  走下面
        return onetext + " " + twotext;
    }else{
        return onetext;
    }
}
console.log(text("猫","狗"));//这里加了参数,他会去把 上面的老虎覆盖掉
console.log(text(undefined,"狐狸"));//第一个也可以  不传任何参数,那么它就会用默认值,但是 第一个必须传

在这里插入图片描述

5、剩余参数

ES6 中,可以使用 ...rest 的方式获取函数中的剩余参数(rest 参数):

//...是剩余操作符,意思就是把1,2,3,4,5,6赋值给item,item就是一个任意类型的数组了
function insert(array:any[],...item:any[]){
    item.forEach(value =>{//遍历每个元素
        array.push(value);//拿到每一个值,插入到array里
    })
}
//注:any[] 指的是任意类型的数组
let array:any[] = [];//定义
insert(array,1,2,3,4,5,6);//调用上面的方法
console.log(array);//[1, 2, 3, 4, 5, 6]

注:看下面这个案例:

function push(array: any[], ...items: any[]) {
	items.forEach(function(item) {
		array.push(item)
	})
}

let a:any[] = []
push(a, 1, 2, 3); //a=[1,2,3]

6、重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。 比如,我们需要实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 hello 的时候,输出反转的字符串 olleh注:实现数字或者字符串的反转。

//在这定义返回值(可以加快反应,不用在进入逻辑后在判断,可以提高性能)
function reverse(x:number):number;//这块代表我们传入数字,返回的也是数字
function reverse(x:string):string;//这块代表我们传入字符串,返回的也是字符串

function reverse(x: number | string): number | string {//这块表示传入数字或者字符串,返回数字或者字符串
	//这个是方法的实现(下面这个有一个缺点,无法确定返回值 类型,解决方法如上)。
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');//这块是返回值
    }
    return x;
}
console.log(reverse(123));//321
console.log(reverse("hello"));//olleh

五、接口

1、接口定义

注:把接口可以理解成,定义一套规范的标准。 在 TypeScript 中,我们使用接口(==Interfaces==)来定义对象的类型。

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。 TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

案例:

// 接口   对 对象的形状进行约束和规范

//接口里面的字段,需要p去规范化,也是以后在只要是人这个类别的,你就需要去遵守我这个规范
interface Person{
    userName:string;
    age:number;
    sayHello():void;//sayHello是个函数,后面跟void,则不需要返回值(也就是return)
}

// 下面这个是对象(也就是你说这个对象是人,那么你里面必须要有名字 和 年龄)
let p:Person={//这块希望p是Person类型的(也就是这个接口类型的)
    //在这个里面把这个Person的形状写出来
    userName:"里斯",//这里用逗号,对象里面都是用逗号 隔开的
    age:22,
    sayHello:():void => {
	
	}
};

console.log(p);//{userName: "里斯", age: 22}

注:赋值的时候,变量的形状必须和接口的形状保持一致(多属性也是不允许的)。

2、可选属性

注:可选属性是可以不存在的,但是还是不能够添加 未定义的属性。

interface Person{
    userName:string;
    age?:number;//这个就是可选属性
}

let p:Person = {
    userName:"张三"
}
console.log(p);//{userName: "张三"}

3、任意属性(索引签名)

interface Person{
    userName:string;
    age?:number;
    // 下面写是 任意类型是字符串
    [prop:string]:any;
    /*  这个就是任意属性(注:any是任意类型),同时prop的类型只能是数字或者字符串,这块写any,
    是因为你要保证上面两个的类型,是他的子类型*/
}

let p:Person = {
    userName:"张三",//userName、age等也可以定义成字符串,也不会报错,因为对象的key 不管你,怎末写都是字符串
    age:89,//这个都可以不写
    prop:88//任意属性,这里可以写数字、字符串等
}

console.log(p);//{userName: "张三", age: 89, prop: 88}

4、只读类型

注:readonly代表只读类型,就是定义后,后期不能在对他进行 修改。

interface Person{
    name:string;
    age:number;
}

let p:Person = {
    name:"张三",
    age:18
}
p.name = "张私";//在这块对p.name进行了改变
console.log(p);//{name: "张私", age: 18}

接下来,我们给 ==name==加一个属性,那么你再去改变值的时候,它就会报错。

interface Person{
    readonly name:string;//这块可以写成可选属性
    age:number;
}

let p:Person = {
    name:"张三",
    age:18
}
// p.name = "张私";//上面加了readonly属性,它就不能在下面这进行 改变了
console.log(p);//{name: "张三", age: 18}

在这里插入图片描述 注: 只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候。

六、

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

TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。

1、类的概念

虽然 JavaScript 中有类的概念,但是可能大多数 JavaScript 程序员并不是非常熟悉类,这里对类相关的概念做一个简单的介绍。

  • 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
  • 对象(Object):类的实例,通过 new 生成
  • 面向对象(OOP)的三大特性:封装、继承、多态
  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat(猫)Dog(狗) 都继承自 Animal(动物),但是分别实现了自己的 eat(吃) 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat(吃),都是同一种,但是有不同的表现形式
  • 存取器(getter & setter):用以改变属性的读取和赋值行为
  • 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
  • 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
  • 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口。

2、回顾ES6中类的用法

1)属性和方法

//Es6中定义类  当我们实例化对象的时候,它默认就会走constructor构造器进行对象的实例化操作。
class Animal{
    // name是属性
    name:string = "";
    constructor(){//加了它,下面打印的是 🐱(这个是类的方法)
        this.name = "🐱";
    }
    // 类里面可以有成员属性,也可以有成员方法
    sayName():string{
        return this.name
    }
}

// 实例化一个对象出来
let cat = new Animal();//这里可以传参
console.log(cat.sayName());//这个打印出来  是一个空
class Animal{
    // name是属性
    name:string = "";
    constructor(name:string){//加了它,下面打印的是 🐱(这个是类的方法)
        this.name = name;
    }
    // 类里面可以有成员属性,也可以有成员方法
    sayName():string{
        return this.name
    }
}

// 实例化一个对象出来
let cat = new Animal("🐱");//这里可以传参
console.log(cat.sayName());
class Animal{
    constructor(public name:string){//在这加public,就代表重构了一个属性,就不用先定义name了
        this.name = name;
    }
    // 类里面可以有成员属性,也可以有成员方法
    sayName():string{
        return this.name
    }
}

// 实例化一个对象出来
let cat = new Animal("🐱");//这里可以传参
console.log(cat.sayName());//🐱

2)类的继承

使用 extends 关键字实现继承,子类中使用 super 关键字来调用父类的构造函数和方法。

class Animal{
    name:string  = "小王";
    // 方法
    sayName():string{
        return this.name;//这里返回的这个this,代表当前的实例
    }
}
//加个extends就继承了 上面的动物类
class Cat extends  Animal{
    
}
let cat = new Cat();
console.log(cat.name);//小王  注:这样就可以访问父的 方法
//就近原则(子有了,就不用夫的了)
class Animal{
    name:string  = "小王";
    // 方法
    sayName():string{
        return this.name;//这里返回的这个this,代表当前的实例
    }
}
//加个extends就继承了 上面的动物类
class Cat extends  Animal{
    name:string = "猫";
}
let cat = new Cat();
console.log(cat.name);//猫
class Animal{
    name:string  = "小王";
    // 方法
    sayName():string{
        return this.name;//这里返回的这个this,代表当前的实例
    }
}
//加个extends就继承了 上面的动物类
class Cat extends  Animal{
    name:string = "猫";
    sayName():string{//子类的方法有了,就用自己的
        return this.name+"^_^";
    }
}
let cat = new Cat();
console.log(cat.sayName());//猫^_^ 注:这块是调用方法,也就是  进行逻辑处理

也可以在子类里面自己额外的添加加属性和方法:

class Animal{
    name:string  = "小王";
    // 方法
    sayName():string{
        return this.name;//这里返回的这个this,代表当前的实例
    }
}
//加个extends就继承了 上面的动物类
class Cat extends  Animal{
    name:string = "猫";
    sayName():string{//子类的方法有了,就用自己的
        return this.name+"^_^";
    }
    sayHello():string{
        return "你好!!!"
    }
}
let cat = new Cat();
console.log(cat.sayName());//猫^_^ 
console.log(cat.sayHello());//你好

子类可以通过super关键字调用父类的方法或者属性

class Animal{
    name:string  = "小王";
    // 方法
    sayName():string{
        return this.name;//这里返回的这个this,代表当前的实例
    }
}
//加个extends就继承了 上面的动物类
class Cat extends  Animal{
    name:string = "猫";
    sayName():string{//子类的方法有了,就用自己的
        return this.name + "^_^" + super.sayName();//super  是一个固定的,它是指父类的 方法(因为 这块的子类定义了属性,所以  它用的 就是子类的属性)
    }
    sayHello():string{
        return "你好!!!"
    }
}
let cat = new Cat();
console.log(cat.sayName());//猫^_^猫
console.log(cat.sayHello());//你好

3)存取器

使用 getter 和 setter 可以改变属性的赋值和读取行为:

//类似与vue的计算属性
class Animal{
    constructor(public name:string){
        // 成员属性name
        this.name = name;
    }
    // 在外面 要用的时候,就使用这个get
    get sayName(){
        return this.name;
    }
    // 改变内部的属性 用set
    set sayName(value:string){
        this.name = value;
    }
}

let animal = new Animal("小明");//这块的参数,传上去
console.log(animal.sayName);//小明
animal.sayName = "小宝";//这个是修改值
console.log(animal.sayName);//小宝

4)静态方法

使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用:

class Animal{
    static animal:string = "动物";
} 

let dog = new Animal();

console.log(Animal.animal);//静态属性,要用类(Animal)去拿(注:不能用实例拿)

注:静态属性和静态方法,只能通过类去调用。

class Animal{
    static animal:string = "动物";
    static isAnimal(animalTwo:any):boolean{/*在这你需要传一个任意类型的变量,
    最后返回一个布尔值*/
        // 用instanceof 去判断是不是某个东西的实例
        return animalTwo instanceof Animal;//这个是静态方法
    }
} 

let dog = new Animal();
console.log(Animal.isAnimal(dog));//true
console.log(Animal.isAnimal("哈哈哈"));//false   因为它不是Animal的实例

5) TypeScript中类的用法

①public private 和 protected

TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 publicprivateprotected

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的。

默认不写这个修饰符,就是用的public修饰的,定义了public类的内外部,都可以正常使用。

//默认的话如果不带修饰符修饰的话,在类内部和外部都是可以访问到类的成员属性与成员方法。
class Animal{
    //属性
    public name:string = "动物";//默认不写这个修饰符,就是用的public修饰的
    // 方法
    public sayName():string{
        return this.name;
    }
}
//创建动物这个类的实例
let animal = new Animal();
console.log(animal.name);//动物

通过private修饰的成员属性和成员方法,只能在类内部可以访问,类的外部与子类都是不能访问的。 在这里插入图片描述 所以上面这个 会报错。 通过protected修饰,在外部是拿不到的(注:这样就和private一样了,但是在继承中,可以看到他俩的区别了) 通过protected修饰的成员属性和成员方法,类内部和子类是可以访问,类的外部是不能访问的。

class Animal{
    //属性
    name:string = "动物";
    // 方法
    protected sayName():string{
        return this.name;
    }
}
// 下面这个是继承
class Cat extends Animal{
    sayHello(){
        return super.sayName();
    }
}
//创建动物这个类的实例
let animal = new Animal();
console.log(animal.name);//动物

②readonly只读属性

只读属性关键字,只允许出现在属性声明或索引签名中(索引签名是接口里面的)。

class Animal{
    readonly name:string = "cat"
    public sayName(){
        console.log("sayName")
    }
}
let animal = new Animal()
animal.name = 'dog' //报错了 name只能读取不能进行修改
console.log(animal.name) 

3、抽象类

abstract 用于定义抽象类和其中的抽象方法。 注:抽象类 也是可以用来定义一些规范的,比如有些类 就提供了这个状态,供别的类 去根据这个状态去相应的改造。 什么是抽象类? 首先,抽象类是不允许被实例化的:

/*一旦一个类里面通过abstract关键字声明了,那么这个类必须通过abstract进行标明。
 * 抽象类是不能被new  进行实例化的
 * 一旦抽象类里面有抽象方法,那么子类在继承抽象类的时候,必须要实现抽象类里面抽象方法。
*/
abstract class Animal{
    name:string = "";
    constructor(name:string){
        this.name = name;
    }
    // Animal的抽象方法
    abstract sayHello():void
}
// 写一个类 去继承这个抽象类,然后再去实现
class Cat extends Animal{
    sayHello():void{
    	//这块不能用super,super只能拿到成员方法
        console.log("这是cat的sayHello的方法" + this.name);//这是cat的sayHello的方法猫
    }
}
//在这在进行实例化
new Cat("猫").sayHello();

七、接口