【万字长文】TypeScript入门指南

2023-12-07 14:57:30 浏览数 (1)

本文主要讲述叶秋学长通过一个月对TypeScript的学习整理出一份完整的入门指南,希望对正在学习前端的小伙伴有所帮助~

基础类型(TS -- 1)

我认为这个TypeScript跟C语言

是很像的,对语言的定义都有严格的规范。

代码语言:typescript复制
let str:string = "这是字符串类型"
//上方我们将str这个变量定义为了string类型,如果对他输入其他类型的内容就会报错,例如:

let str:string = 666
//这个就会报错了,会提示你不能将类型"number"分配给类型"string"

let muban:string = `web${str}`
//我们也可以使用ES6中的模板字符串

let u:void = undefined
let u:void = null
//空值类型能够有这两种内容。void的内容也是不能去赋值给别人的
//某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void

function fnvoid():void{
    return//函数也可以定义为空值,如果定义了void则不能返回内容
}

//undefined跟null类型的也可以交换着用的,具体区别放在了下面

八种内置类型

代码语言:typescript复制
let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;//这里接收的是布尔值,不是布尔值对象(let b:boolean = new Boolean())
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol("me"); 

注意点

null 和 undefined

默认情况下 nullundefined 是所有类型的子类型。 就是说你可以把 nullundefined 赋值给其他类型。

代码语言:javascript复制
// null和undefined赋值给string
let str:string = "666";
str = null
str= undefined

// null和undefined赋值给number
let num:number = 666;
num = null
num= undefined

// null和undefined赋值给object
let obj:object ={};
obj = null
obj= undefined

// null和undefined赋值给Symbol
let sym: symbol = Symbol("me"); 
sym = null
sym= undefined

// null和undefined赋值给boolean
let isDone: boolean = false;
isDone = null
isDone= undefined

// null和undefined赋值给bigint
let big: bigint =  100n;
big = null
big= undefined

如果你在 tsconfig.json 指定了 "strictNullChecks":truenullundefined 只能赋值给 void 和它们各自的类型。

number 和 bigint

虽然 numberbigint 都表示数字,但是这两个类型不兼容。

代码语言:javascript复制
let big: bigint =  100n;
let num: number = 6;
big = num;
num = big;

会抛出一个类型不兼容的 ts (2322) 错误。

任意类型(TS -- 2)

npm install @types/node -D

npm install ts-node -g(装全局的)

类型(任意值) -- any

代码语言:typescript复制
let anys:any = "叶秋学长穿黑丝"

anys = []
anys = 18
anys = {}
anys = Symbol('666')
//any类型就跟原生的是一样的,能够给任意的类型进行定义,所以在在 TypeScript 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的 顶级类型 (也被称作 全局超级类型 )。

作用的地方:

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

在对现有代码进行改写的时候,any 类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。 你可能认为 Object 有相似的作用,就像它在其它语言中那样。 但是 Object 类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法 当你只知道一部分数据的类型时,any 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据

unknown类型

unknown类型比any类型更安全

就像所有类型都可以被归为 any,所有类型也都可以被归为 unknown。这使得 unknown 成为 TypeScript 类型系统的另一种顶级类型(另一种的any

代码语言:typescript复制
let unknow:unknown = {a:():number =>123}
unknow.a()//报错
//unkonwn类型是不能够去调用属性跟函数的,它是 any 类型对应的安全类型

接口和对象类型(TS -- 3)

在 typescript 中,我们定义对象的方式要用关键字 interface(接口),叶秋学长的理解是使用 interface 来定义一种约束,让数据的结构满足约束的格式。

我的理解是interface是一个国企部门只招一个人的话,他们会针对走后门的那个人量身定制招聘要求,到面试的时候,这些条件少一个都不行,多了也不行,毕竟已经内定了,再叼、这些条件不仅满足了而且还会更多的技能也没用,别人就是不要你。(留下心酸的眼泪)

interface类型

代码语言:typescript复制
interface A{
     readonly name:string//这个readonly是只读属性,意思就是说只能读取,不能将其他值赋值给他
    age?:number//这个问号就是可选的意思,条件稍微宽松了一些,下面引用这个age的话有没有这个属性都可以,不会报错
}

let obj:A = {
    name = "叶秋学长嗷嗷叫"//这里如果不写name就会报错,因为我们在上面定义了A类型集合,并且在这个变量中引入了(里面必须要有name属性且类型为字符串)
    age	 = 18
}

注意:这个规定的属性不能多也不能少,参考我上面的案例

可选属性 -- ?操作符

代码语言:typescript复制
interface A{
     readonly name:string
    age?:number//这个问号就是可选的意思,条件稍微宽松了一些,下面引用这个age的话有没有这个属性都可以,不会报错
}

let obj:A = {
    name = "叶秋学长嗷嗷叫"
    age	 = 18//age写不写无所谓
}

任意属性 -- propName:string

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

代码语言:typescript复制
interface Person{
    name:string,
    age?:number,
    [propName:stirng]:string|number//这个属性一旦定义了,引用这个Person的对象就能够写入任意属性,属性的形式主要看冒号后面你定义了什么类型,比如在这里定义的类型就是string和number类型,不是这两者的类型就会报错,包括在Person里面定义除了string跟number之外其他类型也会报错
    //可以理解为这个 [propName:stirng]任意属性的优先度相当高
    
    注意string与number中间的 `|` 符号,小飞棍来咯,这是联合类型,后面笔记会写,这里就当作将string和number类型关系到了一块,有点像逻辑或,满足联合起来的其中一个条件都行,两个也可以
}

只读属性 -- readonly

只读属性必须在声明时或构造函数里被初始化。

代码语言:typescript复制
interface A{
     readonly name:string//这个readonly是只读属性,意思就是说只能读取,不能将其他值赋值给他
}


let obj:A = {
    name = "叶秋学长嗷嗷叫"
}

obj.name = "叶秋学长芳龄18"//报错
console.log(obj)//能够读取
let name1 = obj.name
console.log(name1)

继承属性 -- extends

儿子在前面,父亲在后面。也就是说顺序是 儿子 继承于 父亲

父亲的部分会继承给儿子,父亲的部分如果没有使用?操作符的话,引用儿子的 对象 是必须将父亲的部分都写下去。一说到这个就想到现在有的地方买房子,出政策能够绑定3代人一起还款,父债子还,跑不掉的,连债务都继承了还不能摆脱,这政策太鸡儿黑心了,绝户计

代码语言:typescript复制
interface A{
    name:string
}

interface B extends A{
    age:number
}

let p:B{
    name:"有看到叶秋学长的裤子吗?"
    age:88//两种类型都要写
}

Object与object{} -- 加餐环节

前置知识点补充

原始数据类型(基础数据类型)

中文称呼

引用数据类型

中文称呼

Undefined

未定义

{}

对象

Null

空值

function

函数

Boolean

布尔值

[]

数组

Number

数字

String

字符串

存储位置不同

原始数据类型:直接存储在栈(stack)中的简单数据段,占据空间小,大小固定,属于被频繁使用的数据,所以存储在栈中;

引用数据类型:存储在堆(heap)中的对象,占据空间大,大小不固定,如果存储在栈中,将会影响程序运行的性能。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后,从堆中获得实体。

传值方式不同

  • 基本数据类型:按值传递

不可变 (immutable) 性质:

基本类型是不可变的 (immutable),只有对象是可变的 (mutable). 有时我们会尝试 “改变” 字符串的内容,但在 JS 中,任何看似对 string 值的 "修改" 操作,实际都是创建新的 string 值。任何方法都无法改变一个基本类型的值(在下面的字面量类型中会再次强调)

  • 引用类型:按引用传递

引用类型的值是可变的

引用类型的值是同时保存在栈内存和堆内存中的对象。javascript 和其他语言不同,其不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,那我们操作啥呢? 实际上,是操作对象的引用,引用类型的值是按引用访问的。

objectObject 以及 {} 这三个类型(第三个类型为空对象字面量模式)大家可能不太理解

这集加餐环节就是进行补充,一个冷门但是不邪门的知识点

Object类型

代码语言:typescript复制
//这个类型是跟原型链有关的原型链顶层就是 Object,所以值类型和引用类型最终都指向 Object,所以在TypeScript中Object他包含所有类型。就可以等于任何一个值
//1.数字类型
let a:Object = 123
//字符串类型
let b:Object = "叶秋学长今天没穿裤子"
//数组类型
let c:Object = [1314,520]
//对象类型
let d:Object = {name:"草莓",sex:"女",address:"叶秋学长微信一群"}
//any或者function
let e:Object = ()=> "学姐贴贴"

Object 类型是所有 Object 类的实例的类型。 由以下两个接口来定义:

  • Object 接口定义了 Object.prototype 原型对象上的属性;
  • ObjectConstructor 接口定义了 Object 类的属性, 如上面提到的 Object.create()

object类型

object 代表所有非值类型(非原始类型)的类型,例如 数组 对象 函数等,常用于泛型约束

所有原始类型都不支持,所有引用类型都支持

代码语言:typescript复制
//错误 原始类型(字符串)
let f:object = '努力会获得回报的'
//错误 原始类型(数字)
let g:object = 123
//错误 原始类型(布尔值类型)
let h:object = true
//正确 引用类型(数组类型)
let i:object = [123,"学姐学习Vue3",true]
//正确 引用类型(对象类型)
let j:object = {name:"叶秋学长",identity:['B站UP主','二次元','京东员工','全栈开发工程师'],sex:"女"}
//正确 引用类型(函数类型)
let k:object = ()=>"不要对自己pua,相信自己是最棒的,尊重自己,人生更精彩"

{}字面量类型

看起来很别扭的一个东西 你可以把他理解成 new Object 就和我们的第一个 Object 基本一样 包含所有类型

代码语言:typescript复制
//与Object类型一样
let l:{} = 123//等等,就不写了,跟Object一样
//补充--字面量模式
//这个虽然可以赋值任意类型,赋值结束后,是没办法进行一个修改和增加的操作的

数组类型(TS -- 4)

普通的声明方式

代码语言:typescript复制
//类型加中括号
let arr:number[] = [123]
//这样会报错定义了数字类型出现字符串是不允许的
let arr:number[] = [1,2,3,'1']
//操作方法添加也是不允许的
let arr:number[] = [1,2,3,]

let arr:number[] = [1,2,3,4];//数字类型的数组
let arr2:string[] = ["1","2","3","4"];//字符串类型的数组
let arr3:any[] = [1,"2",true,undefined,[],{}];//任意类型的数组

let arr4:number[][][] = [[[]],[[]],[[]]]
//这个也能够决定你二维数组还是三维数组想要套几层就写几层

泛型 -- Array <类型>

规则 Array <类型>

代码语言:typescript复制
let arr1:Array<number> = [1,2,3,4,5]
let arr2:Array<string> = ["1,2,3,4,5"]
let arr3:Array<boolean> = [true]

//泛型数组套娃写法(还能够决定数组里面数组的类型之类的)
let arr4:Array<Array<number>> = [[123],[456]]

类数组 -- arguments

是所有参数的一个集合

代码语言:typescript复制
function Arr(...args:any):void{//...args为ES6的解构方式,任意类型,voidwei不能有返回值
    console.log(arguments)//输出{'0':4,'1':56,'2':789}
    
    let arr:number[] = arguments//会报错,报缺少类型number[]的以下属性:pop,push,concat,join
    let arr:IArguments = arguments//解决方法
    
    //其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
    interface IArguments {
    [index: number]: any;
    length: number;
    callee: Function;

    }

Arr(4,56,789)

接口表示数组

一般用来描述类数组

代码语言:typescript复制
interface ArrNumber {
    [index: number]: number;//后面的才是定义类型的
    //[index: number]: string;这个就是定义字符串的了
}
let Arr: ArrNumber = [1, 2, 3, 4, 5];
//let Arr: ArrNumber = ["1, 2, 3, 4, 5"];
//表示:只要索引的类型是数字时,那么值的类型必须是数字。

函数扩展(TS -- 5)

函数内参数类型也是可以定义的

代码语言:typescript复制
const fn(name:string,age:number):string{
    return name   age
}
let a = fn('叶秋学长',10000)//输入不符合上述参数内定义的类型就会出错
console.log(a)//输出叶秋学长10000
--------------------------------------------------------------------
const fn(name:string,age:number = 666):string{//如果在下面使用的时候,没有参数传进来就会以你在这里设置的默认参数执行,比如这个666
    return name   age
}
let a = fn('叶秋学长')//输入不符合上述参数内定义的类型就会出错
console.log(a)//输出叶秋学长666
--------------------------------------------------------------------
const fn(name:string,age?:number = 666):string{//也可以使用这个`?`操作符,age传不传就变成可选的了
    return name   age
}
let a = fn('叶秋学长穿女仆装')//输入不符合上述参数内定义的类型就会出错
console.log(a)//输出叶秋学长穿女仆装

对象形式的定义

跟定义对象差不多,但是在针对多个参数的时候会更加的方便,且记得引用的时候要写成({xxxx})形式,不然会报错,输出的是数组形式的

代码语言:typescript复制
interface User{
    name:string;
    age:number
}

const fn(user:User):User{//这里的参数填写方式就变得简单了
    return user
}
let a = fn({
    name:"'叶秋学长",
    age:18
})//输入不符合上述参数内定义的类型就会出错
console.log(a)//输出{name:'叶秋学长',age:18}

函数重载

重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。

如果参数类型不同,则参数类型应设置为 any

参数数量不同你可以将不同的参数设置为可选。

为了让编译器能够选择正确的检查类型,它与 JavaScript 里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。

代码语言:typescript复制
function fn(params:number):void//第一套规则
function fn(params:string,params2:number):void//第二套规则
function fn(params:any,params?:any):void{
    console.log(params)
    console.log(params2)
}

let a = fn(1,1)
//输出1跟undefined,因为遵循的是第一套规则
let a = fn("1",1)
//输出"1"跟1,遵循的是第二套规则

联合类型|类型断言|交叉类型(TS -- 6)

联合类型

联合类型能够让我们可选我们自己需要的类型部分,如果需要的类型超过或者达到2个,那就可以使用。

那为什么不使用any呢?那是因为我们需要的并不是所有类型都能通过,我只希望这两个或者3个类型能够通过,如果需要的类型超过或着达到两个都使用any的话,那就和JavaScript原生没有区别了

代码语言:typescript复制
//例如我们的手机号通常是13XXXXXXX 为数字类型 这时候产品说需要支持座机
//所以我们就可以使用联合类型支持座机字符串
let myPhone: number | string  = '010-820'
 
 
//这样写是会报错的应为我们的联合类型只有数字和字符串并没有布尔值
let myPhone: number | string  = true//报错

函数使用联合类型

这个!!是怎么回事呢?

我们知道一串数字想变成字符串只要加上""就能隐式转换成字符串。

那一个类型只要!就能进行反转,!只有正反,也就是false跟true,这种就有点类似隐式转换了,我们连续转两次就相当于当前形式的布尔值类型了

代码语言:typescript复制
let fn = function(type:number):boolean {
    return !!type//将type强行转化为布尔值类型,如果没用进行转化的话是会报错的
}
--------------------------------------------------------------------
let fn = function(type:number|boolean):boolean {
    return !!type//将type强行转化为布尔值类型,如果没用进行转化的话是会报错的
}

let result = fn(1)
console.log(result);//true

交叉类型

多种类型的集合,联合对象将具有所联合类型的所有成员

代码语言:typescript复制
interface Pople{
    name:string
    age:number
}
interface Man{
    sex:number
}

const yeqiu = (man:Pople & Man):void => {//这里通过了&将Pople跟Man交叉在了一起,则man需要处理Pople也要处理Man。还可以继续跟更多个interface
    console.log(man)
}

yeqiu({
    name:"叶秋学长今天坏掉了"
    age:18
    sex:1//如果sex不写是会报错的,会提示你少写了一个sex
})

类型断言

语法格式,值 as 类型 或者 <类型>值

需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误

覆盖它的推断,并且能以你任何你想要的方式分析它,这种机制被称为「类型断言」。TypeScript 类型断言用来告诉编译器你比它更了解这个类型,并且它不应该再发出错误

S 类型是 T 类型的子集,或者 T 类型是 S 类型的子集时,S 能被成功断言成 T。这是为了在进行类型断言时提供额外的安全性,完全毫无根据的断言是危险的,如果你想这么做,你可以使用 any

2、类型断言的用途

(1)将一个联合类型推断为其中一个类型

(2)将一个父类断言为更加具体的子类

(3)将任何一个类型断言为 any

(4)将 any 断言为一个具体的类型

代码语言:typescript复制
原型:
let fn = function(num:number | string):void{
    console.log(num.length);//这里会报错,因为我们确实没有.length这个内容
}
fn("12345")
--------------------------------------------------------
断言写法
let fn = function(num:number | string):void{
    console.log((num as string).length);//用括号括起来,as断言他是string类型
}
fn("12345")//这样会打印出5
fn(12345)//这样会打印出undefined

另一个例子

代码语言:typescript复制
interface A{
    run:string
}
interface B{
    build:string
}

let fn(type:A | B) =>{
    console.log((<A>type).run);
}

fn({
    build:"123"//这里是没办法传过去的,断言是不能够滥用的,因为我们确实没有.run这个内容
})

临时断言

1.使用any临时断言

代码语言:typescript复制
window.abc = 123
//这样写会报错因为window没有abc这个东西
(window as any).abc = 123
//可以使用any临时断言在 any 类型的变量上,访问任何属性都是允许的。

在下面的例子中,将 something 断言为 boolean 虽然可以通过编译,但是并没有什么用 并不会影响结果,因为编译过程中会删除类型断言

代码语言:typescript复制
function toBoolean(something: any): boolean {
    return something as boolean;
}
 
let bbb =  toBoolean(1);
console.log(bbb)
// 返回值为 1
//

as const

是对字面值的断言,与 const 直接定义常量是有区别的

如果是普通类型跟直接 const 声明是一样的

代码语言:typescript复制
const names = '叶秋学长'
names = 'aa' //无法修改
 
let names2 = '叶秋学长' as const
names2 = 'aa' //无法修改
代码语言:typescript复制
// 数组
let a1 = [10, 20] as const;
const a2 = [10, 20];
 
a1.unshift(30); // 错误,此时已经断言字面量为[10, 20],数据无法做任何修改
a2.unshift(30); // 通过,没有修改指针。之所以没有修改指针是因为const的性质是决定了指针指向的位置是已经固定不会发生改变的了,这个30想要添加进去除非直接修改存储值的地方

内置对象(TS -- 7)

ECMAScript的内置对象

JavaScript 中有很多内置对象,它们可以直接在 TypeScript中当做定义好了的类型。

Boolean、Number、stringRegExpDateError

代码语言:typescript复制
const regexp:Regexp = /wds///声明正则

const date:Date = new Date()//对象类型
//const date:Date = new Date().getTime()  number类型
const error:Error('错误')

总结

代码语言:typescript复制
let b: Boolean = new Boolean(1)
console.log(b)
let n: Number = new Number(true)
console.log(n)
let s: String = new String('叶秋学长今天穿白丝')
console.log(s)
let d: Date = new Date()
console.log(d)
let r: RegExp = /^1/
console.log(r)
let e: Error = new Error("error!")
console.log(e) 

DOM 和 BOM 的内置对象

DocumentHTMLElementEventNodeList

代码语言:typescript复制
const list:NodeList = document.querySelectorAll('#list li')
console.log(list)
//NodeList 实例对象是一个类似数组的对象,它的成员是节点对象。Node.childNodes、document.querySelectorAll () 返回的都是 NodeList 实例对象。 [1]  NodeList 对象代表一个有序的节点列表。

const body:HTMLElement = document.body
console.log(body)

const div:HTMLDivElement = document.querySelector('div')
console.log(div)

document.body.addEventListener('click',(e:MouseEvent)=>{
    console.log(e)
})

//promise
function promise():Promise<number>{//Promise是类型,number是泛型
    return new Promise<number>(resolve,rejuct)=>{
        resolve(1)//如果不进行断言的话会报错
    }
}

promise().then(res=>{
    console.log(res)//返回1,这里会提示你res应该输入number类型
})
代码语言:typescript复制
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
//读取div 这种需要类型断言 或者加个判断应为读不到返回null
let div:HTMLElement = document.querySelector('div') as HTMLDivElement
document.addEventListener('click', function (e: MouseEvent) {
    
});
//dom元素的映射表
interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableDataCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableHeaderCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}

Class类(TS -- 8)

ES6提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的 “类” 改写

JavaScript写法

代码语言:typescript复制
//定义类      JavaScript写法
class Person {
    constructor (name:string,age:number,sub:boolean) {
 		this.name = name
        this.age = age
        this.sub = sub
    }
}

new Person("叶秋学长",22,false)

TypeScript写法

代码语言:typescript复制
//在TypeScript中是需要提前声明类型的
class Person {
    name:string
    age:number
    sub:boolean//没错,没使用标红的是这些
    constructor (name:string,age:number,sub:boolean) {
 		this.name = name
        this.age = age
        this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
    }
}

new Person("叶秋学长",22,false)

public

public内部外部都可以访问,如果定义了public,像p就能够访问constructor内部的变量了。当然,默认情况下也是public

代码语言:typescript复制
//在TypeScript中是需要提前声明类型的
class Person {
    public name:string
    public age:number
    public sub:boolean//没错,没使用标红的是这些
    constructor (name:string,age:number,sub:boolean) {
 		this.name = name
        this.age = age
        this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
    }
}

let p = new Person("叶秋学长",22,false)
p.age p.name p.sub//都可以访问

private

private 私有变量只能在内部访问

代码语言:typescript复制
//在TypeScript中是需要提前声明类型的
class Person {
    private name:string
    private age:number
    private sub:boolean//没错,没使用标红的是这些
    constructor (name:string,age:number,sub:boolean) {
 		this.name = name
        this.age = age
        this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
    }
}

let p = new Person("叶秋学长",22,false)
p.age p.name p.sub//都访问不到了

protected

protected内部和子类中访问

provate跟protectd他们的区别是一个是只能在内部使用,一个是内部与子类访问,例子如下

代码语言:typescript复制
//在TypeScript中是需要提前声明类型的
class Person {
    protected name:string
    private age:number
    public sub:boolean//没错,没使用标红的是这些
    constructor (name:string,age:number,sub:boolean) {
 		this.name = name
        this.age = age
        this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
    }
}

class Man extends Person{
    constructor(){
        super("叶秋学长",22,false)
        this.name
        this.sub//这两个都可以访问到,this.age访问不到。因为age是private,private只能在内部使用而不能在子类访问,Man是Person的子类
    }
}

let p = new Person("叶秋学长",22,false)
p.age p.name p.sub

static 静态属性 和 静态方法

静态属性和非静态属性的区别:

在内存中存放的位置不同:所有 static 修饰的属性和方法都存放在内存的方法区里,而非静态的都存在堆内存中 出现的时机不同:静态属性和方法在没创建对象之前就存在,而非静态的需要在创建对象才存在 静态属性是整个类都公用的 生命周期不一样,静态在类消失后被销毁,非静态在对象销毁后销毁 用法:静态的可以直接通过类名访问,非静态只能通过对象进行访问

使用static注意事项

带静态修饰符的方法只能访问静态属性 非静态方法既能访问静态属性也能访问非静态属性 非静态方法不能定义静态变量 静态方法不能使用 this 关键字 静态方法不能调用非静态方法,反之可以

父子类中静态和非静态的关系

对于非静态属性,子类可以继承父类非静态属性,但是当父子类出现相同的非静态属性时,不会发生子类的重写并覆盖父类的非静态属性,而是隐藏父类的非静态属性 对于非静态方法,子类可以继承并重写父类的非静态方法 对于静态属性,子类可以继承父类的静态属性,但是如何和非静态属性一样时,会被隐藏 对于静态方法,子类可以继承父类的静态方法,但是不能重写静态方法,同名时会隐藏父类的

注:静态属性、静态方法、非静态属性都可以被继承和隐藏,但是不可以被重写,非静态方法可以被重写和继承

静态代码块的作用: 一般情况下,有些代码需要在项目启动的时候就执行,这时候就需要静态代码块,比如一个项目启动需要加载配置文件,或初始化内容等。 静态代码块不能出现在任何方法体内 对于普通方法:普通方法是需要加载类 new 出一个实例化对象,通过运行这个对象才能运行代码块,而静态方法随着类加载就运行了。 对于静态方法:在类加载时静态方法也加载了,但是必须需要类名或者对象名才可以访问,相比于静态代码块,静态方法是被动运行,而静态代码块是主动运行 静态代码块不能访问普通变量 普通变量只能通过对象调用的,所以普通变量不能放在静态代码块中。

普通代码块和构造代码块

静态代码块和构造代码块在声明上少一个 static 关键字 执行时机: 构造代码块在创建对象时被调用,每次创建对象都会调用一次,且优先于构造函数执行。 注:不是优先于构造函数执行,而是依托于构造函数,如果不创建对象就不会执行构造代码块 普通代码块和构造代码块的区别在于,构造代码块是在类中定于的,而普通代码块是在方法体中定义的,执行顺序和书写顺序一致。

执行顺序

**静态代码块 > 构造代码块 > 构造函数 > 普通代码块**

代码语言:typescript复制
class Person {
    protected name:string
    private age:number
    public sub:boolean//没错,没使用标红的是这些
    static aaa:string = '123456'//静态属性
    
    constructor (name:string,age:number,sub:boolean) {
 		this.name = name
        this.age = age
        this.sub = sub//上面定义了变量就需要使用,如果没用使用的话声明的变量就会标红(就算不标红不提示,真运行下去也会报错),不能就那么放着,要么就用上,要么就给他个默认值0塞着
        this.run()//会报错,调用不了。互斥的,不能够通过静态函数去访问内部的变量,或者是在内部的变量去调用外部的静态函数
        Person.run()//只能这样去调用
    }
    
    static run (){
        this.dev()//静态函数之间可以互相调用
        this.aaa//用this的话只能访问上面static类型的,其他的不管是public还是private或者是protected都是不能够访问的(会报不存在属性的错误)   因为这里的this指的是当前这个类,而构造函数里面的this指的是新的实例对象
        return '789'
    }
    
    static dev(){
        this.aaa//静态函数之间可以互相调用
        return 'dev'
    }
    
}

console.log(Person.run())//返回789

Person.aaa//能够直接访问,不需要再new一下
console.log(Person.aaa)
let p = new Person("叶秋学长",22,false)

interface 定义 类

ts interface 定义类 使用关键字 implements 后面跟 interface 的名字多个用逗号隔开 继承还是用 extends

通过接口去约束类

代码语言:typescript复制
interface Person{
    run(type:boolean):boolean
}

class Man implements Person{//会提示我们Man中缺少属性run,但类型Person中需要该属性
    
}
代码语言:typescript复制
//通过接口去约束类
interface Person{
    run(type:boolean):boolean
}

interface H{
    set():void
}

class Man implements Person,H{//会报错,提示我们缺少set属性
 	   run(type:boolean):boolean{
           return type
       }
}
代码语言:typescript复制
interface Person{
    run(type:boolean):boolean
}

interface H{
    set():void
}

class A{//也可以使用继承去使用
    params:string
    constructor(params){
        this.params = params
    }
}

class Man extends A implements Person,H{
 	   run(type:boolean):boolean{
           return type
       }
    	set(){
            //啥也没有,这就是用接口去描述类
        }
}

抽象类(TypeScript8)

用关键词abstract修饰的类称为 abstract 类(抽象类

应用场景如果你写的类实例化之后毫无用处此时我可以把他定义为抽象类

或者你也可以把他作为一个基类 -> 通过继承一个派生类去实现基类的一些方法

对于 abstract 方法只允许声明,不允许实现(因为没有方法体)(毕竟叫抽象,当然不能实实在在的让你实现),并且不允许使用 final 和 abstract 同时修饰一个方法或者类,也不允许使用 static 修饰 abstract 方法。也就是说,abstract 方法只能是实例方法,不能是类方法。

代码语言:typescript复制
abstract class A{
    name:string
    construct(name:string){//construct:构造器
        this.name = name
    }
    //abstract getName(){//方法getName不能具有实现,因为它标记为抽象。定义抽象类的函数
//    return 213
//}
    setName(name:string){
        this.name = name
    }
    abstract getName():string//抽象类
    
    
}

class B extends A{//派生类。定义了抽象类必须在派生类里实现
        //B类是继承A类的,此时A类就是一个抽象类
    constructor(){
        super('叶秋学长')
    }
    getName():string{
        return this.name
    }
}
//此时A类是无法被创建实例的(new A),也就是无法创建抽象类的实例
//B类是可以创建实例的(new B)

let b = new B
b.setName("叶秋学长2")//通过抽象类的设置,成功修改掉子类的内容
//    setName(name:string){
//        this.name = name
//    }
console.log(b.getName())

我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!

0 人点赞