背景
TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。它非常适用于一些大型项目,也非常适用于一些基础库,极大地帮助我们提升了开发效率和体验。(不要卷,不要卷,我怕控制不住自己)
常用语法
基础类型
(() => {
//(1)布尔类型
//基本语法 let 变量名:数据类型 = 值
let flag: boolean = true;
console.log("flag====>", flag);
//(2)数字类型
let a1: number = 10; // 十进制
let a2: number = 0b1010; // 二进制
let a3: number = 0o12; // 八进制
let a4: number = 0xa; // 十六进制
console.log(a1, a2, a3, a4);
//(3)字符串类型
let str: string = "测试一下";
console.log("str====>", str + a1);
//ts变量一开始是什么类型,name后期赋值的时候只能用这个类型的数据,是不允许用其他类型的数据赋值给当前的这个变量
//(4)null和undefined类型
let und: undefined = undefined;
let nll: null = null;
//undefined和null都可以作为其他类型的子类型,把undefined和null赋值给其他类型的变量,如:number类型的变量
/* let a5:number = undefined
let a6:number = null */
//(5)数组类型
//数组定义方式1 let 变量名:数据类型[] = [值1,值2,值3]
let arr1: number[] = [1, 2, 4];
//数组定义方式2 let 变量名:Array<number> = [值1,值2,值3]
let arr2: Array<number> = [4, 5, 6];
//注意:数组定义后,里面的数据的类型必须和定义数组的时候的类型是一致的,否则会有错误提示,也不会编译
//(6)元组类型
let arr3: [string, number, boolean] = ["小明", 100, true];
//注意:元组类型在使用的时候,数据的类型的位置和数据的个数应该和在定义元组的时候的数据的类型及位置是一致的
console.log(arr3[0].split(""));
//console.log(arr3[1].split('')); //数据类型是number,调用字符串的方法会报错
//(7)枚举类型,枚举里面的每个数据值都可以叫做元素,每个元素都有自己的编号,编号是从0开始的,依次的递增1
enum Color {
red,
green,
yellow,
}
let color: Color = Color.red;
console.log(color); //可以通过元素拿到编号,编号的值可以修改
console.log(Color[2]); //可以通过编号拿到元素
//(8)any类型
let any1: any = 100;
any1 = "胃口不好";
console.log(any1);
//当一个数组中要存有多种类型的数据,个数不确定,类型不确定,此时也可以使用any类型来定义数组
let arr4: any[] = [100, "测试", false];
//这种情况下没有错误提示
//(9)void类型,表示没有任何类型
//在函数声明的时候,小括号的后面使用:void,代表的是该函数没有任何返回值
function showMsg(): void {
console.log("只要学不死,就往死里学");
}
showMsg();
let vd1: void = undefined;
console.log(vd1);
//(10)object类型
//定义一个函数,参数是object,返回值也是object
function getObj(obj: object): object {
return {
name: "测试",
age: 12,
};
}
getObj({ age: 12 });
let a = new String("测试");
getObj(a);
//(11)联合类型(Union Types)表示取值可以为多种类型中的一种
//需求1:定义一个函数得到一个数字或字符串的字符串形式值
function getString(str: number | string) {
return str.toString();
}
console.log(3);
console.log("测试");
//需求2:定义一个函数的到一个数字或字符串的长度
//类型断言:告诉编译器,我知道自己是什么类型,也知道自己在干什么
//类型断言的语法方式1:<类型>变量名
//类型断言的语法方式2:值 as 类型
function getLength(str: number | string): number {
// return str.toString().length
if ((<string>str).length) {
// return (<string>str).length
return (str as string).length;
} else {
return str.toString().length;
}
}
console.log(getLength(12));
console.log(getLength("测试"));
//(12)类型推断,ts会在没有明确指定类型的时候推断出一个类型
/* let text = 100
text = '测试'
console.log(text); */
let text; //any类型
text = "测试";
text = 100;
console.log(text);
})();
接口
//接口是对象的状态(属性)和行为(方法)的抽象(描述)
//接口是一种类型,是一种规范,是一种规则,是一个能力,是一种约束
(() => {
//定义一个接口,该接口作为person对象类型使用,限定或约束该对象中的属性数据
interface IPerson {
readonly id: number; //readonly只读属性
name: string;
age: number;
sex?: string; //?可选属性,可以有,也可以无
}
//如果作为变量使用的话使用const,如果是作为属性使用的话用readonly
//定义一个对象,该对象的类型就是定义的接口IPerson
const person: IPerson = {
id: 1,
name: "测试",
age: 12,
sex: "男",
};
//person.id =2 //只读属性无法修改
})();
接口继承和实现
(()=>{
interface Person{
eat():void;
}
//extends关键字用于继承接口
interface Child extends Person{
play():void;
}
//implements关键字用于接口实现
class Boy implements Child {
public name: string;
constructor(name:string){
this.name = name;
}
eat(){
console.log(this.name+'喜欢吃馒头')
}
play(){
console.log(this.name+'写代码');
}
}
var boy = new Boy('老曹');
boy.eat();
})()
函数类型
//为了使用接口表示函数类型,我们需要给接口定义一个调用签名
//它就像是只有参数列表和返回值类型的函数定义,参数列表离的每个参数都需要名字和类型
(() => {
//函数类型:通过接口的方式作为函数的类型来使用
interface ISearchTunc {
//定义一个调用签名
(source: string, subString: string): boolean;
}
//定义一个函数,该类型就是上面定义的接口
const searchString: ISearchTunc = function (
source: string,
subString: string
): boolean {
return source.search(subString) > -1;
};
//调用函数
console.log(searchString("啧啧,试一试咯", "试"));
})();
类类型
//类:可以理解为模板,通过模板可以实例化对象
//面向对象的编程思想
(() => {
//ts中类的定义及使用
class Person {
//定义属性
name: string;
age: number;
gender: string;
constructor(
name: string = "曹贼",
age: number = 12,
gender: string = "男"
) {
this.name = name;
this.age = age;
this.gender = gender;
}
//实例化方法
sayHi(str: string) {
console.log(`${this.name}---${this.age}----${this.gender}`, str);
}
}
//ts中使用类,实例化对象,可以直接进行初始化操作
const person = new Person("狗", 12, "zz");
person.sayHi("???");
})();
类型使用
类
继承
//继承:类与类之间的关系
//继承后类与类之间的叫法
//A类继承了B类,那么此时A叫做子类,B叫做基类
//子类----->派生类
//基类----->超类(父类)
(() => {
//定义一个类
class Person {
//定义属性
name: string;
age: number;
gender: string;
//定义构造函数
constructor(name: string, age: number, gender: string) {
//更新数据
this.name = name;
this.age = age;
this.gender = gender;
}
//实例化方法
sayHi(str: string) {
console.log(`${this.name}====>`, str);
}
}
//定义一个类,继承至Person
class Student extends Person {
level: number;
constructor(name: string, age: number, gender: string, level: number) {
super(name, age, gender);
this.level = level;
}
sayHi(str: string) {
console.log("我想修改一下试试");
super.sayHi("哈哈" + str);
}
}
const person = new Person("小明", 12, "男");
person.sayHi("啧啧");
const student = new Student("小花", 13, "女", 1);
student.sayHi("<{=....(嘎~嘎~嘎~)");
//总结:类和类之间如果要有继承关系,需要使用extends关键字
//子类可以调用父类中的构造函数,使用的是super关键字(包括调用父类中的实例方法,也可以使用super)
//子类中可以重写父类中的方法
})();
多态
//多态:父类型的引用指向子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为
(() => {
//定义一个父类
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
run(distance: number = 1) {
console.log(`跑了${distance}米`);
}
}
//定义一个子类
class Dog1 extends Animal {
//构造函数
constructor(name: string) {
//调用父类的构造函数,实现子类中属性的初始化操作
super(name);
}
//实例化方法,重写父类中的实例方法
run(distance: number = 5) {
console.log(`跑了${distance}米`);
}
}
//定义一个子类
class Dog2 extends Animal {
//构造函数
constructor(name: string) {
//调用父类的构造函数,实现子类中属性的初始化操作
super(name);
}
//实例化方法,重写父类中的实例方法
run(distance: number = 10) {
console.log(`跑了${distance}米`);
}
}
// 实例化父类对象
const ani: Animal = new Animal("动物");
ani.run(100);
const dog1: Dog1 = new Dog1("大黄");
dog1.run();
const dog2: Dog2 = new Dog2("大黄");
dog2.run();
//父类和子类的关系:父子关系,父类类型创建子类的对象
const dog3: Animal = new Dog1("大黄");
dog1.run();
const dog4: Animal = new Dog2("大黄");
dog2.run();
//定义一个函数,参数是Animal类型
function showRun(ani: Animal) {
ani.run();
}
showRun(dog1);
showRun(dog2);
})();
修饰符
// 修饰符(类中的成员的修饰符):主要是描述类中的成员(属性,构造函数,方法)的可访问性
// 类中的成员都有自己默认的访问修饰符,public
// public修饰符---公共的,类中成员默认的修饰符,代表的是公共的,任何位置都可以访问类中这个成员的数据
// private修饰符---私有的,类中的成员如果使用private来修饰,那么外部是无法访问这个成员数据的,子类也不行
// protected修饰符---受保护的,类中的成员如果使用protected来修饰,那么外部是无法访问这个成员数据,当然,子类中是可以访问该成员数据的
(() => {
//定义一个类
class Person {
//属性
public name: string;
//private name: string;
//protected name: string;
public constructor(name: string) {
//更新属性
this.name = name;
}
//方法
eat() {
console.log("这是一个小测试");
}
}
//定义一个子类
class Student extends Person {
// 构造函数
constructor(name: string) {
super(name);
}
play() {
console.log("卷起来", this.name);
}
}
//实例化方法
const per = new Person("大黄");
// console.log(per.name);
const stu = new Student("GG");
console.log(stu.name);
})();
//readonly修饰符:首先是一个关机键字,对类中的属性成员进行修饰,修饰后,该属性成员,就不能在外部被随意修改了
//构造函数中,可以对只读的属性成员的数据进行修改
//如果构造函数中没有任何参数,类中的属性成员已经使用readonly进行修饰了,name外部也是不能对这个属性值进行修改的
(() => {
//定义一个类
class Person {
//属性
// readonly name:string = 'JJ' //初始值
readonly name: string;
//构造函数
constructor(name: string) {
this.name = name;
}
sayHi() {
console.log("测试一下", this.name);
//类中的普通方法也是不能修改readonly修饰的成员属性值
// this.name = '大甜甜'
}
}
console.log(Person.name);
//实例化对象
const person: Person = new Person("GG");
console.log(person);
console.log(person.name);
//此时无法修改,因为name属性是只读的
// person.name = 'HH'
console.log(person.name);
console.log("---------------------------");
///readonly 修饰类中的构造函数中的参数(参数属性)
class Animal {
//构造函数
//构造函数中的name参数,一旦使用readonly进行修饰后,name该name参数可以叫参数属性
//构造韩数中的name参数,一旦使用readonly进行修饰后,那么Animal中就有一个那么的属性成员
/* constructor(readonly name:string='小甜甜'){
// this.name = name
} */
//构造函数中的name参数,一旦使用public进行修饰后,那么类中就有了一个公共的name属性
/* constructor(public name:string='小甜甜'){
// this.name = name
} */
//构造函数中的name参数,一旦使用private进行修饰后,那么类中就有了一个私有的name属性
/* constructor(private name:string='小甜甜'){
// this.name = name
} */
//构造函数中的name参数,一旦使用protected进行修饰后,那么类中就有了一个受保护的name属性(只能在本类或者派生类中使用)
constructor(protected name: string = "小甜甜") {
// this.name = name
}
}
//实例化对象
const ani = new Animal("大甜甜");
console.log(ani);
// ani.name = 'GG'
// console.log(ani.name);
})();
存储器
// 存取器:让我们可以有效的控制对 对象中的成员的访问,通过getters和setters来进行操作
(() => {
//外部可以传入姓氏和名字数据,同时使用set和get控制姓名的数据,外部也可以惊醒修改操作
class Person {
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
//姓名的成员属性(外部可以访问,也可以修改)
//读取器----负责读取数据
get fullName() {
console.log("get中......");
//姓名=====> 姓氏和名字的拼接
return this.firstName + "_" + this.lastName;
}
//设置器----负责设置数据(修改)
set fullName(val) {
console.log("set中.....");
//姓名=====> 把姓氏和名字获取到的重新赋值给firstName和lastName
let names = val.split("_");
this.firstName = names[0];
this.lastName = names[1];
}
}
//实例化对象
const person = new Person("东方", "不败");
console.log(person);
//获取改属性成员的属性
console.log(person.fullName);
//设置该属性成员的属性
person.fullName = "诸葛_孔明";
console.log(person.fullName);
})();
静态成员
//静态成员:在类中通过static修饰的属性或者方法,name就是静态成属性及静态的方法,也称之为静态成员
//静态成员在使用的时候是通过类名.的这种方式来调用的
(() => {
class Person {
//类中默认有一个内置的name属性,所以直接定义静态的name会出想错误提示信息
static name1: string = "GG";
constructor() {
//此时this是实例对象,name1是静态属性,不能通过实例对象直接调用静态属性来使用
//this.name1 = name
}
//静态方法
static sayHi() {
console.log("hh");
}
}
//实例化对象
//const person = new Person()
//通过实例对象调用的属性(实例属性)
// console.log(person.name1);
//通过实例对象调用的方法(实例方法)
// person.sayHi()
//通过类名.静态属性的方式来访问该成员数据
console.log(Person.name1);
//通过类名.静态属性的方式来设置该成员数据
Person.name1 = "KK";
console.log(Person.name1);
//通过类名.静态方法的方式来调用内部的静态方法
Person.sayHi();
})();
抽象类
//抽象类:包含抽象方法(抽象方法一般没有任何的具体内用的实现),也可以包含实例方法,抽象类型不能被实例化,为了让子类进行实例化及实现内部的抽象方法
(() => {
//定义一个抽象类
abstract class Animal {
//抽象属性,没有必要
abstract name: string;
//抽象方法
abstract eat(): void;
//报错的,抽象方法不能有具体的实现
/* abstract eat(){
console.log('测试');
} */
//实例方法
sayHi() {
console.log("kk");
}
}
//定义一个子类(派生类)
class Dog extends Animal {
name: string = "hh";
//重新的实现抽象类中的方法,此时这个方法就是当前的Dog中的实例方法了
eat() {
console.log("测试一下");
}
}
//实例化Dog的对象
const dog: Dog = new Dog();
dog.eat();
console.log(dog.name);
//不能实例化抽象类的对象
// const ani = new Animal()
})();
函数
//函数:封装了一些重复使用的代码,在需要的时候直接调用即可
(() => {
// js中的书写方式---ts中同样适用
//函数声明,命名函数
// function add(x, y) {
// return x + y;
// }
//函数表达式,匿名函数
// const add2 = function (x, y) {
// return x + y;
// };
//函数声明,命名函数
//函数中的x和y参数的x和y都是number类型的,小虎括号后面的:number,代表的是该函数的返回值也是number类型的
function add(x: number, y: number): number {
return x + y;
}
console.log(add(1, 2));
//函数表达式,匿名函数
//函数中的x和y参数的x和y都是string类型的,小虎括号后面的:string,代表的是该函数的返回值也是string类型的
const add2 = function (x: string, y: string): string {
return x + y;
};
//函数的完整的写法
// const add3:类型 = function(x:类型,y:类型):类型{}
// add3---->变量名---->函数add3
// (x:string, y:string)=>string 当前这个函数的类型
// function (x:string, y:string):string { return x + y; } 相当于符合上面的这个函数类型的值
const add3: (x: string, y: string) => string = function (
x: string,
y: string
): string {
return x + y;
};
})();
可选参数和默认参数
//可选参数:函数在声明的时候,内部的参数使用了?进行修饰,那么就表示该参数可传可不传
//默认参数:函数在声明的时候,内部的参数有自己的默认值,此时的这个参数就可以叫做默认参数
(() => {
//定义一个函数:传入姓氏和名字,可以得到姓名(姓氏+名字=姓名)
//需求:如果不传入任何内容,那么就返回默认的姓氏
//需求:如果只传入姓氏,那么就返回姓氏
//需求:如果传入姓氏和名字,那么就返回姓名
const fullName = function (
firstName: string = "默认的",
lastName?: string
): string {
if (lastName) {
return firstName + "_" + lastName;
} else {
return firstName;
}
};
//函数调用
//什么也不传入
console.log(fullName());
//只传入姓氏
console.log(fullName("姓氏"));
//传入姓氏和名字
console.log(fullName("诸葛", "孔明"));
})();
剩余参数
//剩余参数(rest参数)
(() => {
//...args:string[]---->剩余参数放在了一个字符串数组中
function showMsg(str: string, ...args: string[]) {
console.log(str); //'a'
console.log(args); //['b','c']
}
showMsg("a", "b", "c");
})();
函数重载
//函数重载:函数名字相同,函数的参数及个数不同
(() => {
//定义一个函数
//需求:我们有一个add函数,他可以接收2个string类型的参数进行拼接,也可以接收2个number类型的参数进行相加
//函数重载声明
function add(x: string, y: string): string;
function add(x: number, y: number): number;
// 函数声明
function add(x: string | number, y: string | number): string | number {
if (typeof x === "number" && typeof y === "number") {
return x + y;
}
if (typeof x === "string" && typeof y === "string") {
return x + y;
}
}
//函数调用
//两个参数都是字符串
console.log(add("测试", "一下"));
//两个参数都是数字
console.log(add(1, 2));
//此时如果传入的是非法数据,ts应该给我们提示错误信息
// console.log(add(1,'测试'));
})();
泛型
//泛型:在定义函数,接口,类的时候不能预先确定要使用的数据的类型,而是在使用函数,接口,类的时候才能够确定数据类型
(() => {
//需求:定义一个函数,传入两个参数,第一个参数是数据,第二个参数是数量,函数的作用:根据数量产生对应个数的数据,存放在一个数组中
//定义一个函数
function getArr1(val: number, count: number): number[] {
//根据数据和数量产生一个数组
const arr: number[] = [];
for (let i = 0; i < count; i++) {
arr.push(val);
}
return arr;
}
const arr1 = getArr1(99.99, 3);
console.log(arr1);
//定义一个函数,同上,只不过传入的是字符串类型
function getArr2(val: string, count: number): string[] {
//根据数据和数量产生一个数组
const arr: string[] = [];
for (let i = 0; i < count; i++) {
arr.push(val);
}
return arr;
}
const arr2 = getArr2("测试", 3);
console.log(arr2);
//可以传入任意类型的数据,返回来的是存储这个任意数据的数组
/* function getArr3(val: any, count: number): any[] {
//根据数据和数量产生一个数组
const arr: any[] = [];
for (let i = 0; i < count; i++) {
arr.push(val);
}
return arr;
}
const arr30 = getArr3('测试', 3);
const arr31 = getArr3(999.333, 3);
console.log(arr31[0].toFixed(2)); //没有任何智能提示(要么有方法名字的提示,要么有错误的提示信息)
*/
function getArr4<T>(val: T, count: number): T[] {
//根据数据和数量产生一个数组
const arr: T[] = [];
for (let i = 0; i < count; i++) {
arr.push(val);
}
return arr;
}
const arr40 = getArr4<number>(200.123, 4);
const arr41 = getArr4<string>("ccc", 4);
console.log(arr40[0].toFixed(2));
})();
多个泛型参数
//多个泛型参数
(() => {
function getMsg<T, K>(val1: T, val2: K): [T, K] {
return [val1, val2];
}
const arr1 = getMsg<string, number>("测试", 1);
console.log(arr1);
console.log(arr1[0].split(""));
})();
泛型接口
//泛型接口:在定义接口时,为接口中的属性或方法定义泛型类型,在使用接口时,在指定具体的泛型类型
(() => {
//需求:定义一个类,用来存储用户的相关信息(id,名字,年龄)
//通过一个类对的实例对象调用add方法可以添加多个用户信息对象,调用getUserId方法可以根据id获取某个指定的用户信息对象
//定义一个接口
interface IBaseCRUD<T> {
data: Array<T>;
add: (val: T) => T;
getUserId: (val: number) => T;
}
//定义一个用户信息的类
class User {
id?: number; //用户id,?代表改属性可选
name: string; //用户姓名
age: number; //用户年龄
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
//定义一个类,可以针对用户的信息对象进行增加和查询操作
//CRUD---->create,read,update,delete
class UserCRUD implements IBaseCRUD<User> {
data: Array<User> = [];
add(user: User): User {
//产生id
user.id = Date.now() + Math.random();
//把用户信息对象添加到data数组中
this.data.push(user);
return user;
}
//方法根据id查询指定的用户信息对象
getUserId(id: number): User {
return this.data.find((user) => user.id === id);
}
}
//实例化添加用户信息对象的类UserCRUD
const userCRUD: UserCRUD = new UserCRUD();
//调用添加数据的方法
userCRUD.add(new User("jack", 20));
userCRUD.add(new User("tom", 90));
//解构user对象中的id
const { id } = userCRUD.add(new User("lucy", 67));
console.log(id);
userCRUD.add(new User("rousi", 55));
console.log(userCRUD.data);
//根据用户id查询对象信息
const user = userCRUD.getUserId(id);
console.log(user);
})();
泛型类
(()=>{
//定义一个类,类中的属性值得类型是不确定,方法的参数及返回值的类型也是不确定
//定义一个泛型类
class GenericNumber<T>{
//默认属性的值得类型是泛型类型
defaultValue:T
add:(x:T,y:T)=>T
}
//在实例化类的对象的时候,爱确定泛型的类型
const g1:GenericNumber<number> = new GenericNumber<number>()
//设置属性值
g1.defaultValue = 100
//相加方法
g1.add = function(x,y){
return x+y
}
console.log(g1.add(g1.defaultValue,100));
//在实例化类的对象的时候,爱确定泛型的类型
const g2:GenericNumber<string> = new GenericNumber<string>()
//设置属性值
g2.defaultValue = '测试'
//相加方法
g2.add = function(x,y){
return x+y
}
console.log(g2.add(g2.defaultValue,'哈哈'));
})()
泛型约束
//如果我们直接对一个泛型参数去length属性,会报错,因为这个泛型根本就不知道它有这个属性
(() => {
//定义一个接口,用来约束捡来的摸个类型中必须要有length这个属性
interface ILength {
//接口中有一个属性length
length: number;
}
function getLength<T extends ILength>(x: T): number {
return x.length;
}
console.log(getLength<string>("测试一下"));
})();
内置对象
(() => {
//ECMAScript 的内置对象
let b: Boolean = new Boolean(1);
let n: Number = new Number(true);
let s: String = new String("abc");
let d: Date = new Date();
let r: RegExp = /^1/;
let e: Error = new Error("error Message");
const div: HTMLElement = document.getElementById("tset");
const divs: NodeList = document.querySelectorAll("div");
document.addEventListener("click", (event: MouseEvent) => {
console.dir(event.target);
});
const fragement: DocumentFragment = document.createDocumentFragment();
})();
补充知识
声明文件
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。很多的第三方库都定义了对应的声明文件库, 库文件名一般为 @types/xxx。有的第三库在下载时就会自动下载对应的声明文件库(比如: webpack),有的可能需要单独下载(比如jQuery/react)
声明文件: 把声明语句(即定义操作)放到一个单独的文件(xxxx.d.ts)中, ts会自动解析到项目中所有声明文件
- 引入第三方库(import xxxx from 'xxxx')
- 定义操作(declare var JQuexxxxry:(selector:string)=>any)
- 使用(xxxx('选择器'))
结语
学习视频是在B站上找的,跟着视频敲得笔记,感谢作者的分享。唉,真·不是在被卷死,就是在被卷死的路上。