前端入门(六)TypeScript

TS简介

TypeScript是JavaScript的超集,可以声明变量类型,TypeScript = Type + JavaScript

Ts相对于Js增加了:

  • 类型
  • ES不具备的新特性
  • 支持ES新特性
  • 有丰富的配置选项
  • 配套的强大开发工具

在这里插入图片描述

TS与JS的区别

在这里插入图片描述

JavaScriptTypeScript
动态语言具有静态语言的特点
编译性语言运行时报错编译期间报错
弱类型语言,没有类型强类型语言,类似java, C++等,定义时指明类型
不支持模块、接口、泛型支持模块、接口、泛型
基本数据类型和引用数据类型更多的基本数据类型和引用数据类型,如any, never, enum等
在浏览器中直接执行编译为js后才能在浏览器进行执行

优缺点

优势:

  • 更早发现错误
  • 任何位置都是代码提示,有类型推断机制,增加开发效率
  • 类型系统提升了代码的可维护性,重构更容易

劣势:

  • 学习成本,需理解接口、泛型、类型等知识
  • 和有些库的结合不是很完美,集成到自动构建流程中需要额外的工作量。

TS环境安装与初体验

  1. 下载Node.js
    ○ 64位:https://nodejs.org/dist/v14.15.1/node-v14.15.1-x64.msi
    ○ 32位:https://nodejs.org/dist/v14.15.1/node-v14.15.1-x86.msi
  2. 安装Node.js
  3. 使用npm全局安装typescript
    ○ 进入命令行
    ○ 输入:npm i -g typescript
  4. 创建一个ts文件
  5. 使用tsc对ts文件进行编译
    ○ 进入命令行
    ○ 进入ts文件所在目录
    ○ 执行命令:tsc xxx.ts

js是弱类型语言,导致出错时,无法快速定位问题源头和原因:
在这里插入图片描述
而使用ts,在编译出错时,编译器会很方便地告知问题源头和原因:
在这里插入图片描述

TS数据类型

类型声明

类型声明是TS非常重要的一个特点 :

  • 通过类型声明可以指定TS中变量(参数、形参)的类型
  • 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错

简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值

let 变量: 类型;
// 数值类型 number
let money:number; // 定义了一个名称叫做money的变量, 这个变量中将来只能存储数值类型的数据
money = 20;

let 变量: 类型 =;
let money1:number = 25;
// 当然由于TS可以类型推导 还可以向下边这样写
let money2 = 25;  // money2 将被自动推导为number类型



function fn(参数: 类型, 参数: 类型): 类型{
    ...
}

TS中主要有以下数据类型:
在这里插入图片描述

基本数据类型 number- bigint - boolean - string

  • number类型: 双精度 64 位浮点值。它可以用来表示整数和分数。

  • bigint类型:表示特别大的数。

  • boolean类型:表示逻辑值:true 和 false。

  • string类型:一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式

export default {}

// 数值类型 number
let money:number; // 定义了一个名称叫做money的变量, 这个变量中将来只能存储数值类型的数据
money = 20;
// money = "200000"; // 会报错
// 注意点: 其它的用法和JS一样
// money = 0x00;
// money = 0o00;
// money = 0b00;
console.log(money); 

// bight类型: 表示非常大的数
// ES2020可用

const Hundred1: bigint = BigInt(100)
const Hundred2: bigint = 100n

// 布尔类型 boolean
let flag:boolean;
flag = true;
// flag = 1; // 会报错  c语言
console.log(flag);

// 字符串类型 string
let beauty:string;
beauty = "李一桐";
let dream = `我的女神是${beauty},为了她,我想月入${money}k`;
console.log(dream); 

// 总结:
数值,字符串和布尔值是我们开发中最常使用的基础数据类型,与js中的数值,字符串和布尔完全一致,在ts中我们主要做类型校验使用

数组

数组:声明变量的一组集合称之为数组。

export default {}

// 数组类型
// 方式一
// 表示定义了一个名称叫做 beautyList 的数组, 这个数组中将来只能够存储字符串类型的数据
let beautyList:string[]; 
beautyList = ['李嘉欣', '王祖贤', '邱淑贞'];
// arr2 = ['李嘉欣', '王祖贤', '邱淑贞', 200000]; // 报错
console.log(beautyList); 

// 方式二
// 表示定义了一个名称叫做 moneyList 的数组, 这个数组中将来只能够存储数值类型的数据
let moneyList:Array<number>;  
moneyList = [10, 30, 500];
// moneyList = ['a', 30, 500]; // 报错
console.log(moneyList);


// 方式三 联合类型
// 表示定义了一个名称叫做 dream 的数组, 这个数组中将来既可以存储数值类型的数据, 也可以存储字符串类型的数据
let dream:(number | string)[];
dream = [10, 30, 500, "李一桐", "赵露思", "白鹿"];
// dream = [10, 30, 500, "李一桐", "赵露思", "白鹿", false]; // 报错
console.log(dream);


// 方式四 任意类型
// 表示定义了一个名称叫做 arbitrarily 的数组, 这个数组中将来可以存储任意类型的数据
let arbitrarily:any[]; 
arbitrarily = [100, '关之琳', true];
console.log(arbitrarily);

数组是我们前端开发过程中,最常用的引用类型之一,在发送请求获取响应时,我们往往会使用到数组类型,
因此我们务必要掌握好数组的几种定义方式

元组 - tuple

TS中的元祖类型其实就是数组类型的扩展

元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。

export default {}

// 元祖类型 Tuple
// TS中的元祖类型其实就是数组类型的扩展
// 元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同

// 表示定义了一个名称叫做 tup1 的元祖, 这个元祖中将来可以存储3个元素, 
// 第一个元素必须是字符串类型, 第二个元素必须是数字类型, 第三个元素必须是布尔类型
let tup1:[string, number, boolean]; 
tup1 = ['宋祖儿', 100, false];
// tup1 = ['宋祖儿', 100, true, 200]; // 超过指定的长度会报错
// tup1 = [100,"宋祖儿", true];
// tup1 = ['杨超越', 100, true];
console.log(tup1); 

总结:
定义: [‘’, ‘’, …]
作用:元祖用于保存定长定数据类型的数据

any 与 void

  • any: 表示任意类型, 当我们不清楚某个值的具体类型的时候我们就可以使用any

  • void: 当一个函数没有返回值时,你通常会见到其返回值类型是 void

export default {}

// any类型
// any表示任意类型, 当我们不清楚某个值的具体类型的时候我们就可以使用any
// 在TS中任何数据类型的值都可以负责给any类型

// 使用场景一
// 变量的值会动态改变时,比如来自用户的输入,任意值类型可以让这些变量跳过编译阶段的类型检查
let salary: any = 1800;    // 数字类型
salary = 'my salary is 18k';    // 字符串类型
salary = false;    // 布尔类型

// 使用场景二
// 改写现有代码时,任意值允许在编译时可选择地包含或移除类型检查
let x: any = 4;
x.ifItExists();    // 正确,ifItExists方法在运行时可能存在,但这里并不会检查
x.toFixed();    // 正确 

// 使用场景三
// 定义存储各种类型数据的数组时
let beautyList: any[] = [1, false, 'fine'];
beautyList[1] = 100;



// void类型
// 某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 
// 当一个函数没有返回值时,你通常会见到其返回值类型是 void
// 在TS中只有null和undefined可以赋值给void类型
function makeMoney(): void {
  console.log("I want to make much money and marry a wife!!!");
  // return "100K beauty" // 报错
}

makeMoney()

let value:void; 
// 定义了一个不可以保存任意类型数据的变量, 只能保存null和undefined
// value = 100; // 报错
// value = "杨紫";// 报错
// value = true;// 报错
// 注意点: null和undefined是所有类型的子类型, 所以我们可以将null和undefined赋值给任意类型
// 严格模式下会null报错
// value = null; // 不会报错  
value = undefined;// 不会报错

null与undefined

  • TypeScript里,undefinednull两者各自有自己的类型分别叫做undefinednull。 和 void相似,它们的本身的类型用处不是很大
  • 非严格模式下,可以把 null和undefined赋值给number类型的变量。
export default {}

// TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。 
// 和 void相似,它们的本身的类型用处不是很大

let x: undefined = undefined;
let y: null = null;

let money:number = 100;


// 非严格模式下,可以把 null和undefined赋值给number类型的变量。
money = y;
money = x;

never与object

never类型:

表示的是那些永不存在的值的类型;

never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;

变量也可能是 never类型,当它们被永不为真的类型保护所约束

object类型

object表示非原始类型,也就是除numberstringbooleansymbolnullundefined之外的类型

定义了一个只能保存对象的变量

我们后面更常用的是 接口类型别名

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

// 注意点:never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。
//  即使 any也不可以赋值给never

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

error("鞠婧祎");

// 推断的返回值类型为never
function fail() {
  return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
  while (true) {
  }
}


// Object类型
// 表示一个对象
// 定义了一个只能保存对象的变量
let goddess:object; 
// goddess = 1;
// goddess = "123";
// goddess = true;
goddess = {name:'白鹿', age:27};
console.log(goddess);

枚举 - enum

  • enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
// 枚举用于表示固定的几个取值
// 例如: 人的性别只能是男或者女

enum Gender{ 
  Male,
  Femal
}

// 定义了一个名称叫做val的变量, 这个变量中只能保存Male或者Femal
let val:Gender; 
val = Gender.Male;
val = Gender.Femal;
// val = 'nan'; // 报错
// val  = false;// 报错


// 注意点: TS中的枚举底层实现的本质其实就是数值类型, 所以赋值一个数值不会报错
val = 666; // 不会报错
console.log(Gender.Male); // 0
console.log(Gender.Femal);// 1

// 注意点: TS中的枚举类型的取值, 默认是从上至下从0开始递增的
//         虽然默认是从0开始递增的, 但是我们也可以手动的指定枚举的取值的值
// 注意点: 如果手动指定了前面枚举值的取值, 那么后面枚举值的取值会根据前面的值来递增
enum Gender2{ 
  Male=5,
  Femal
}
console.log(Gender2.Male); // 5
console.log(Gender2.Femal);// 6

// 注意点: 如果手动指定了后面枚举值的取值, 那么前面枚举值的取值不会受到影响
enum Gender3{ 
  Male,
  Femal=10
}
console.log(Gender3.Male); // 0
console.log(Gender3.Femal);// 10

// 注意点: 我们还可以同时修改多个枚举值的取值, 如果同时修改了多个, 那么修改的是什么最后就是什么
enum Gender4{ 
  Male=100,
  Femal=200
}
console.log(Gender4.Male); // 100
console.log(Gender4.Femal);// 200

// 我们可以通过枚举值拿到它对应的数字
console.log(Gender.Male); // 0
// 我们还可以通过它对应的数据拿到它的枚举值
console.log(Gender[0]); // Male

全局唯一引用 - symbol

const firstName = Symbol("name")
const secondName = Symbol("name")

if (firstName === secondName) {  // 始终为false,因为symbol为全局唯一,即便两个变量定义方式一模一样,它们也是不相等的。
	console.log('ok')
}

变量声明与结构

// 变量声明的方式
// var | let | const

// 数组解构
let goddess = ["邱淑贞", "赵雅芝"];
let [first, second] = goddess;
console.log(first); // 邱淑贞
console.log(second); // 李紫婷



let [third, ...rest] = ["赵今麦", "蒋依依", "欧阳娜娜", "李庚希"];
console.log(third); //  赵今麦
console.log(rest); // ["蒋依依", "欧阳娜娜", "李庚希"];


let [, fourth, , fifth] = [1, 2, 3, 4];
console.log(fourth); // 2
console.log(fifth); // 4



// 对象解构
let beauty  = {
  uname: "杨超越",
  age: 20,
  sex: "女",
}

let { uname, age} = beauty;
console.log(uname);
console.log(age);

类型断言

TypeScript中的类型断言是一种将变量或表达式的类型强制转换为开发者指定的类型的方式。可以使用尖括号(<>)语法或者as语法进行类型断言。

let str: any = "hello";
let len1: number = (<string>str).length;


let str: any = "hello";
let len2: number = (str as string).length;

使用场景:

  • 1、消除类型检查错误,有些情况下,开发者明确知道某个变量的类型,但是TypeScript的类型检查器并不能推断出这个类型。这时可以使用类型断言,将变量的类型强制转换为开发者指定的类型,从而消除类型检查错误。
  • 2、处理联合类型,当一个变量的类型是多种类型的联合类型时,如果开发者想要使用其中一个类型的属性或方法,可以使用类型断言将其转换为该类型,以便进行后续操作。
  • 3、处理any类型,有时开发者需要使用any类型的变量,但是any类型会降低代码的类型安全性。如果能够明确知道该变量的类型,可以使用类型断言将其转换为该类型,以提高代码的类型安全性。

type起别名

类型别名就是给一个类型起个新名字, 但是它们都代表同一个类型
有4种方式:

  • 定义值的别名
  • 定义函数别名
export default {}

// 第一种,定义值的别名,使用时,只能是定义时提供的值
type beautys = "邱淑贞" | "赵雅芝" | "王祖贤" | "朱茵"
let one:beautys;

one = "邱淑贞";
// one = 100 // 报错


// 第二种,定义函数别名
type myfun = (a:number, b:number) => number;

let fun:myfun = (a:number, b:number) => a + b;
fun(10, 20);


// 第三种,定义类型别名
type myGoddass = {
  name: string,
  age: number,
  sex: string,
  actor?: boolean
}

let shuzhen:myGoddass = {
  name: "邱淑贞",
  age: 18,
  sex: "女"
}

// 第四种,定义联合类型别名
type str = string | number


let s: str = 123

let s2: str = '123'

console.log(s,s2);

TS面向对象

1.类的基本使用

定义类的关键字为 class,后面紧跟类名,类可以包含以下几个模块(类的数据成员):

  • 字段 − 字段是类里面声明的变量。字段表示对象的有关数据。
  • 构造函数 − 类实例化时调用,可以为类的对象分配内存。
  • 方法 − 方法为对象要执行的操作。
class Person {
  // 注意点: 需要先定义实例属性,才能够使用
  name: string
  age: number

  constructor(name: string, age: number){
    this.name = name;
    this.age = age;
  }

  sayHello(): void{
    console.log(`我的女神是${this.name}, 她今年${this.age}岁了, 但是在我心里她永远18岁!`);
  }

}

let p = new Person("邱淑贞", 54);
p.sayHello();

2.类的继承

TypeScript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类。

类继承使用关键字 extends,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。

TypeScript 一次只能继承一个类,不支持继承多个类,但 TypeScript 支持多重继承(A 继承 B,B 继承 C)。

语法格式如下:class child_class_name extends parent_class_name

export default {}


class Person {
  name: string
  age: number

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  say():void{
    console.log(`我是${this.name}, 今年${this.age}`);
    
  }
}


class Student extends Person {
  score: string;

  constructor(name: string, age: number, score: string) {
    super(name, age);
    this.score = score;
  }

  say():void {
    // 调用父类的函数
    super.say();
    console.log(`我是重写之后的say方法, 我是学生${this.name}, 今年${this.age}岁了, 我的成绩为${this.score}`);
  }
}

let s = new Student("蒋依依", 18, "A");
s.say();

3.static与instanceof

  • static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。

  • instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false。

export default {}

// static关键字
// static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。
class StaticTest {
  static salary: string;

  static say(): void {
    console.log("我们想要的工资是: " + StaticTest.salary);
    
  }
}

StaticTest.salary = "18k";
StaticTest.say();


// instanceof运算符
// instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false。
class Person{} 
let p = new Person() 
let isPerson = p instanceof Person; 
console.log("p 对象是 Person 类实例化来的吗? " + isPerson); // true

class Student extends Person {}
let s = new Person() 
let isPerson2 = s instanceof Person; 
console.log("s 对象是 Person 类实例化来的吗? " + isPerson2); // true

4.类中的修饰符

  • public(默认):公有,可以在任何地方被访问
  • protected: 受保护,可以被其自身以及其子类访问
  • private: 私有,只能被其定义所在的类访问。
  • readonly: 可以使用 readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
export default {}

class Person {
  public name: string;
  protected age: number;
  private sex: string;

  constructor(name: string, age: number, sex: string) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }

  say():void {
    console.log(`我的名字是${this.name},性别为${this.sex}, 今年${this.age}岁了,`);
  }
}

class Student extends Person {
  score: string
  constructor(name: string, age: number, sex: string, score: string) {
    super(name, age, sex);
    this.score = score;
  }
  show():void {
    console.log(this.name);
    console.log(this.age);
    // console.log(this.sex);
    console.log(this.score);
    
  }
}

let p = new Person("邱淑贞", 18, "女");
p.say();

let s = new Student("王心凌", 18, "女", "A");
s.show();

// 思考题: 如果我们给 constructor 加上 protected 会出现什么情况?


// readonly: 字段的前缀可以是 readonly 修饰符。这可以防止在构造函数之外对该字段进行赋值。
class PrintConsole {
  readonly str1: string = "HTML, CSS, JS, VUE REACT, NODE"
  readonly str2: string;
  readonly str3: string;
  readonly str4: string;

  constructor(str2: string, str3:string, str4:string) {
    this.str2 = str2;
    this.str3 = str3;
    this.str4 = str4;
  }
  // show():void {
  //   this.str2 = "123"
  // }
}

let pc = new PrintConsole("我的头发去哪了, 颈椎康复指南", 
                          "35岁失业该怎么办, 外卖月入一万也挺好", 
                          "活着") 

5.getter与setter

官方的另外一个名字: 存取器

通过getters/setters来截取对对象成员的访问

注意点:

如果存在 get ,但没有 set ,则该属性自动是只读的
如果没有指定 setter 参数的类型,它将从 getter 的返回类型中推断出来
访问器和设置器必须有相同的成员可见性

export default {}

class GetNameClass {
  private _fullName: string = "倪妮"

  get fullName():string {
    console.log("我是get方法");
    return this._fullName
  }

  set fullName(newName:string) {
    console.log("我是set方法");
    this._fullName = newName;
  }
}


let starname = new GetNameClass();
starname.fullName = "袁冰妍"

console.log(starname);

console.log(starname.fullName);

6.抽象类

  • 定义

    抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化

    抽象类是专门用于定义哪些不希望被外界直接创建的类的

    抽象类和接口一样用于约束子类

  • 抽象类和接口区别

    抽象方法必须包含 abstract关键字并且可以包含访问修饰符

    接口中只能定义约束, 不能定义具体实现。而抽象类中既可以定义约束, 又可以定义具体实现

export default {}

abstract class Person {
  abstract name: string;
  abstract show(): string;

  showName() {
    console.log(this.show());
  }
}


class Student extends Person {
  name: string = "孟子义";
  show():string {
    return "陈情令"
  }
}


// let p = new Person();
let s = new Student();
let res =  s.show();
console.log(res);

7.implements子句

  • 类可以实现接口,使用关键字 implements
  • 可以使用一个 implements 子句来检查一个类,是否满足了一个特定的接口。如果一个类不能正确地
    实现它,就会发出一个错误

注意点:

​ 实现一个带有可选属性的接口并不能创建该属性

​ 只要一个接口继承了某个类, 那么就会继承这个类中所有的属性和方法,但是只会继承属性和方法的声明, 不会继承属 性和方法实现

extends的区别

extends: 继承某个类,继承之后可以使用父类的方法,也可以重写父类的方法

implements:继承某个类,必须重写才可以使用

export default {}

/* 
  extend: 继承某个类,继承之后可以使用父类的方法,也可以重写父类的方法
  implements:继承某个类,必须重写才可以使用
*/

interface IPersonInfo {
  name: string;
  age: number;
  sex?: string; 
  show(): void;
}

interface IMusic {
  music: string
}

class Person implements IPersonInfo, IMusic {
  name: string = "吴谨言";
  age: number = 32;
  music: string = "雪落下的声音";
  show() {
    console.log(`${this.name}是'延禧攻略'的主演,她今年${this.age}岁了`);
    console.log(`${this.name}唱了一首歌叫 ${this.music}`);
    
  }
}
let p = new Person();
p.show();
// p.name = "周冬雨"
// p.sex = "女" // 报错


// 注意点: 只要一个接口继承了某个类, 那么就会继承这个类中所有的属性和方法
// 但是只会继承属性和方法的声明, 不会继承属性和方法实现

interface ITest extends Person {
  salary: number
}

class Star extends Person implements ITest {
  salary: number = 50;
  name: string = "关晓彤";
  age: number = 18;
}


let s = new Star();
console.log(s.salary);
console.log(s.name);

8.类的初始化顺序

  • 基类的字段被初始化
  • 基类构造函数运行
  • 子类的字段被初始化
  • 子类构造函数运行
export default {}

/* 
  1.基类的字段被初始化
  2.基类构造函数运行
  3.子类的字段被初始化
  4.子类构造函数运行
*/

class Old {
  name: string = "林青霞"
  constructor() {
    console.log("我的名字是:" + this.name);
  }
}

class Young extends Old {
  name: string = "李子璇"
  constructor () {
    super()
    // console.log(this.name);
  }
}

let y = new Young();
  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值