0


TypeScript学习笔记

TypeScript学习笔记

TypeScript概述和环境

TypeScript(简称:TS)是JavaScript的

超集

(JS有的TS都有)。

TypeScript = Type + JavaScript(为JS添加了

类型系统


安装解析TS的工具包typescript

image-20210703202831765
安装步骤:

  1. 打开VSCode终端。

  2. 输入安装命令:npm i -g typescript 敲回车,来安装(注意:需要联网)。

    typescript
    

    :就是用来解析TS的工具包。提供了tsc命令,实现了TS —> JS的转化。

    npm
    

    :用来安装前端开发中用到的包,是安装Node.js时自动安装的。

    i
    

    (install):表示安装。

    -g
    

    (–global):全局标识,可以在任意目录中使用该工具。

  3. 安装查看: tsc -v / tsc --version


安装简化执行TS的工具包 ts-node

  • 这个工具包内部会先用tsc命令, 把TS文件转为JS文件
  • 内部再使用命令ts-node,用来执行生成的JS代码。

安装命令:npm i -g ts-node

使用方式:ts-node hello.ts

  • 后期在脚手架中使用ts就不会这么麻烦, 初学语法暂时使用这个方式

入门代码

创建 hello.ts 文件,console.log(“hello TS”)

在终端进行执行:tsc hello.ts

会生成一个 hello.js 文件


TS中的注释

// 单行/* 多行 */

JS原有类型

  • 限定了数据类型后,后续的赋值必须符合属性类型,否则编译报错!
  • 基础类型如果首字母大写, 赋值的时候可以采用实例化的形式 - let flag: Boolean = new Boolean(true)
  • 以下,1-5为常用的,1-3为重点

number

数值类型- number(整数和浮点数)

除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。

let decLiteral:number=6;let hexLiteral:number=0xf00d;let binaryLiteral:number=0b1010;let octalLiteral:number=0o744;

string

  • string 单引号(推荐)或双引号或``都可以
let name:string="bob";
name ="smith";

boolean

  • 布尔值 boolean true/false值
let isDone:boolean=false;

undefined

  • 表示此处应该有值, 但是现在没有值
  • 申明了一个变量, 但是未赋值, 这个变量就是undefined
  • 在TS中, 可以给已有类型的基本数据类型变量, 重新赋值undefined
let u:undefined=undefined

null

  • 表示此处不应该有值, 没有值了
  • 在TS中, 可以给已有类型的引用数据类型变量, 重新赋值null
let n:null=null

object

对象类型基本使用

  • js中属于对象的太多,添加object后等于是没有添加类型限制,开发很少用
  • 示例1:// {}也表示对象,表示变量o必须是一个对象类型,在其中可以指定属性类型// 以下示例表示对象o中只能有指定的属性名称和类型,以及个数// 在属性名后面加?,表示该属性可选let o:{ name:string, age:number, sex?:string};o ={name:'孙悟空',age:18}// oko ={name:'孙悟空'}// 报错,少了一个age属性o ={name:'孙悟空',age:18,sex:'男'}// ok示例2:// 需求:在一个对象中,我只有一个属性是有要求的,其他的属性名称、类型、个数都未知let o:{ name:string,[propertyName:string]:any}/** 以上代码,[propertyName: string] 表示属性名是string(对象的属性名都是字符串类型)* [propertyName: string]: any 表示该对象中的属性是任意类型,* 如果any是string,表示该对象中的属性都必须是string类型*/实例3:functiongetObj(obj: object):object {// do something return{ name:"卡卡西"}}console.log(getObj({name:"佐助"}))console.log(getObj(newString('字符串')))

对象类型中的函数写法

指定对象中, 函数的类型

  1. 写法一: sayHi(name:string):void``````const user:{ name:stringsayHi(name:string):void} user ={ name:'张三'sayHi(name){console.log('你好,我叫'+ name)}}
  2. 写法二: add(n1:number, n2:number) => number``````const user:{ name:stringadd(n1:number, n2:number)=>number}user ={ name:'李四'add:function(a, b){return a+b }}

array

  • 数组; 开发中, 数组中的元素为同一类型
  • 语法1:类型[]``````// 表示数值数组let list:number[];let list:Array<number>;let list:number[]=[1,2,3];
  • 语法2:使用泛型写法,Array<元素类型>// 表示字符串数组let list:string[];let list:Array<string>let list:Array<number>=['a','b','c'];

扩展: 申明二维数组

let arr:number[][]=[[1,2,3],[7,8,9]]

function

函数类型的定义

  • 函数主要是限制出入参的类型及个数, 并无固定的针对性语法, 实现方式多种
  1. 单独限制出入参类型- 箭头函数的写法常用// 箭头函数let addFunc =(num1:number, num2:number):number=>{return num1 + num2}addFunc=function(n1, n2):number{return n1 + n2;}console.log(addFunc(2,6))// 8***************************************************// 函数声明functionadd(num1:number, num2:number):number{return num1 + num2}console.log(add(2,2))// 4
  2. 使用类型别名typeAddFun=(num1:number, num2:number):numberconst add:AddFun=(num1, num2)=>{return num1 + num2}- 注意: 只适合函数表达式// 函数表达式letfun1=function(){}// 函数声明functionfun2(){}
  3. 使用接口- 为了使用接口表示函数类型,需要给接口定义一个调用签名interfaceISearchFunc{// 定义调用签名 (source:string, subString:string):boolean}// 定义一个函数,该类型即为上面的接口const searchString:ISearchFunc=function(source:string, subString:string):boolean{return source.search(subString)>-1}// 调用console.log(searchString('道阻且长,行则将至','长'))// true

扩展 - 函数的参数

默认值

function(name: string, age: number =18){}

可选参数

  • 添加 ?,接口也适用(表示属性可省略)
  • 在入参变量名后面添加 ? , 注意可选参数必须放在最后, 可以是一个或多个可选参数
  • 函数体中对可选参数需使用非空校验相关逻辑
// 表示入参age, sex,可省略function(name: string, age?: number, sex?: string){}

TS新增类型

any

  • 任意类型(any可略) , 尽量不要使用any
  • 设置为该类型后等于对变量关闭了TS的类型检测, 直接让它们通过编译阶段的检查
  • 可以赋值给任意类型变量
  • // eg1:多次赋值均不报错let a:any=123a ="str"a =true// eg1:它可以赋值给其他任意类型,使其他类型也称为any类型// 简单理解就是b是any类型,经过下面赋值,使a也成了any类型let b:number=123b = a

unknown

  • 未知类型的值; 编码中,尽量使用unknown代替any

  • 和any的异同点1. 同:都是可以多次赋值不同类型的值2. 异:any可以赋值给任意任意类型的值,使其也称为any的值,但是unknown不能直接赋值其他类型的值

  • 一般来说,这个类型并不是开发者手写的,是网络传来的,需要配和断言使用(在使用的时候需要明确这个变量的类型,可以多次指定类型)typeA={name:string}typeB={age:number}// 模拟ajax传递过来的数据let c:unknown=JSON.parse("{'name':"Tom"}")let var1 =(c asA).namelet var2 =(c asB).age


void

  • 空值 , 用于函数返回值 表示函数没有返回值(常用)
  • 实际编码中,其实可以return null或者return undefined,但是没有意义,应该是语法上的兼容而已
functionfn():void{// do something     return;// 或者不写return}

never

  • 永不存在的值的类型
  • 开发中使用较少,一般用于抛出异常、无限循环的函数返回类型
  • 出现该类型的时候,注意检查代码是否有问题

语法:

// 返回never的函数必须存在无法达到的终点functionerror(message:string):never{thrownewError(message);}// 推断的返回值类型为neverfunctionfail(){returnerror("Something failed");}// 返回never的函数必须存在无法达到的终点functioninfiniteLoop():never{while(true){}}

eg:

typeCode=1|2|3|undefinedlet dir:Code // 表示dir的取值只能是”1,2,3,undefined“ 四者之一switch(dir){case1:break;case2:break;case3:break;caseundefined:break;default:console.log('如果进入该分支,表示dir的值不在”1,2,3,undefined“中, 即为never类型')}

元组

  • 固定长度的数组(元素类型可以不一致)
  • 结合数组理解, 最大的区别的是元组的长度固定, 且每个索引对应的元素类型固定
  • 对于值, 可以get, set, update, 注意变更的值只能是规定的类型, update可以用arr[0] = newValue, set可以用arr.push(newValue)// 表示数组中只能有2个string的元素,长度不可变let arr1:[string,string]let arr2:[number,string]

enum

  • 枚举; 默认枚举值有编号,类似数组索引,从0开始

eg1:

enum Gender {     
    Male,     
    Female
}let tom: Gender = Gender.Male
console.log(tom)// 0console.log(Gender.Male, Gender.Female)// 0 1
  • 特殊使用方式:

如果枚举值是数字值, 可以用以下写法

enum Color {  
    Red =6,  
    Green =7,  
    Blue =8}let c:string= Color[6]// Red

联合类型

  • 把多个类型联合为一个类型 (表示取值可以为多种类型中的一种)
  • | 隔开
// 表示入参param可以是number或者string类型// 出参为string类型functiongetSomeThing(param:number|string):string{return param+''}getSomeThing(123)getSomeThing("字符串")
typeMyType=1|2|3|4|5;let a: MyType =2;let b: MyType =4;
// 数组中,既可以有字符串,也可以有数字let arr:(number|string)[]

arr =[123,'哈哈']a
rr =[66,88,99]

typeof 操作符

用来获取一个变量或对象的类型

interfacePerson{  
    name:string;  
    age:number;}const test: Person ={ name:"Tom", age:20};typeSys=typeof test;// type Sys = Person

在上面代码中,我们通过 typeof 操作符获取 test 变量的类型并赋值给 Sys 类型变量,之后我们就可以使用 Sem 类型:

const Vivo: Sys  ={ name:"Lili", age:3}

也可以对嵌套对象执行相同的操作:

const kakaxi ={    
    name:"kakaxi",    
    age:27,    
    address:{      
        province:'湖南',      
        city:'长沙'}}typeKakaxi=typeof kakaxi;/*    以下即为Kakaxi类型 
    type Kakaxi = {    
        name: string;    
        age: number;    
        address: {        
            province: string;        
            city: string;    
        };
    }
*/

此外, typeof 操作符除了可以获取对象的结构类型之外,它也可以用来获取函数对象的类型,比如

functiontoArray(x:number):Array<number>{return[x];}typeFunc=typeof toArray;// -> (x: number) => number[]

类型断言

断言语法

  • 同java中的类型转换
  • 关于断言是用来跳过编译检查的语法
  • 主要用于当 TypeScript 推断出来类型并不满足你的需求,你需要手动指定一个类型。
  • 只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。

有两种语法,关键字

as

和标签

<>

两种,

  • <类型>值
  • 值 as 类型

由于

<>

会与

JSX

语法冲突,**建议统一使用

as

来进行类型断言**。

as

语法(推荐):

let someValue:any="this is a string";let strLength:number=(someValue asstring).length;
<>

语法:

let someValue:any="this is a string";let strLength:number=(<string>someValue).length;

断言扩展

非空断言 !

  • ! 表示此处一定有值, 跳过类型检查

如果编译器不能够去除 null 或 undefined,可以使用非空断言

!

手动去除。

functionfixed(name:string|null):string{functionpostfix(epithet:string){return name!.charAt(0)+'.  the '+ epithet;// name 被断言为非空  }  
    name = name ||"Bob"returnpostfix("great")}

代码解释:

第 2 行,

postfix()

是一个嵌套函数,因为编译器无法去除嵌套函数的 null (除非是立即调用的函数表达式),所以 TypeScript 推断第 3 行的

name

可能为空。

第 5 行,而

name = name || "Bob"

这行代码已经明确了

name

不为空,所以可以直接给 name 断言为非空(第 3 行)。


双重断言

双重断言极少有应用场景,只需要知道有这种操作即可:

interfaceUser{  
    nickname:string,  
    admin:boolean,  
    group:number[]}const user ='Evan'asanyas User

代码解释: 最后一行,使用 as 关键字进行了两次断言,最终变量 user 被强制转化为 User 类型。


扩展 - 类型的其他用法

类型别名

  • 为任意类型取别名, 语法: type 别名 = 类型
  • 使用场景: 当同一类型(复杂)被多次使用时, 可以通过类型别名, 简化对该类型的编码
  • 需要使用关键字 type, 别名命名, 建议首字母大写
typeCustomArray=(number|string)[]let arr1:CustomArray =[1,'啊',6]let arr2:CustomArray =['x','y',8]

一个变量指定多个类型

// 表示a可以是string或number类型let a:string|number;

扩展&使用

let j:{name:string}&{age:number}

j ={    
    name:'卡卡西',  
    age:25}

j ={name:'鸣人'}// 报错,删了一个age属性

继承、实现概述

extends

  • 调用父类使用super
  • 子类继承父类的属性和方法
  • 子类可以改写父类的属性和方法

类的继承

只能单继承, 但是可以多级继承

classA{    
    name:string;constructor(name:string){this.name = name    
    }}classBextendsA{    
    age:number;constructor(age:number, name:string){// 必须先用super调用父类构造器        super(name)this.age = age    
    }}classCextendsB{     
    sex:string;constructor(sex:string, age:number, name:string){super(age,name)this.sex = sex    
    }}const instance =newC('张三',18,'男')console.log(instance)// C { name: '男', age: 18, sex: '张三' }

接口的继承

接口可以多继承

interfaceA{    
    id:number}interfaceB{    
    code:string}interfaceCextendsA,B{    
    isFlag:boolean}const test:C={    
    id:200,    
    code:'凌凌漆',    
    isFlag:false}console.log(test)// { id: 200, code: '凌凌漆', isFlag: false }

implements

  • 实现是对接口、抽象类中规定的方法, 进行具体的编码
  • 实现可以多实现

类、接口、抽象类

在TS中, 这三者的概念, 和Java中一样, 这里只用最直白简洁的语言进行阐述.


  • 类最单纯的理解可以看做是代码的封装, 好比数组是储存数据, 而类可以存储的更多, 还可以存储代码

接口

  • 简单理解成一种约束、规则.
  • 主要通过定义抽象方法, 让继承的子类去实现
  • 试着理解如果引入一个插件, 需要创建一个类, 写固定的方法进行配置, 在插件里就可以通过定义接口, 让用户实现该接口, 进而约束用户进行固定配置的编码

##抽象类

  • 开发不常用
  • 里面既可以有具体方法, 也可以有抽象方法
  • 既是对子类, 公共代码的封装; 也是对子类, 需要具体编码的方法的约束
  • 可以结合上述两者的功能, 对其理解

接口及接口中属性的扩展

接口

  • 类比java,可看做一种强制性的规范,一种约束
interfaceIPerson{    
    firstName:string    
    lastName:string}functionshowFullName(person:IPerson){return`${person.firstName}_${person.lastName}`}let kakaxi:IPerson {    
    firstName:'旗木'  
    lastName:'卡卡西'}console.log(showFullName(kakaxi))// 旗木_卡卡西

接口继承

  • 和类的继承是一个概念
  • TS支持继承多个接口interfacepoint2D{ x:number y: Number }interfacepoint3D{ z:number}interfacepoint4Dextendspoint2D, point3D { w:number}const pointTest: point4D ={ x:100, y:200, z:300, w:400}console.log(pointTest)

扩展 - 只读和可省略

  • readonly 只读
  • ? 可省略
interfaceIAnimals{// id是只读的number类型    readonly id:number    
    name:string    
    age:number// sex可省略    
    sex?:string}const dog:IAnimals ={    
    id:1,    
    name:'来福',    
    age:2,    
    sex:'男'}

类、继承、super

类的概念

  • 类比Java
  • 类的类型,通过接口定义
  • 一个类可以同时实现多个接口
  • 类里面定义构造函数contructor,是为了实例化的时候对属性值进行初始化
  • 接口和接口之间:继承
  • 类和接口之间:实现
interfaceIFly{    
    fly:()}// 定义一个类,这个类的类型就是上面定义的接口classPersonimplementsIFly{fly(){console.log("飞上天,和太阳肩并肩")}}const p1 =newPerson()
p1.fly()
interfaceIFly{    
    fly:()}interfaceISwim{    
    swim:()}classPersonimplementsIFly,ISwim {fly(){console.log("会飞天")},swim(){console.log("会游泳")}}const p2 =newPerson()
p2.fly()
p2.swim()
interfaceTMyFlyAndSwimextendsIFly,ISwim {}classPersonimplementsTMyFlyAndSwim{fly(){console.log("会飞天")},swim(){console.log("会游泳")}}const p3 =newPerson()
p2.fly()
p2.swim()

继承

  • 同比Java
  • super关键字可以调用父类的构造器,也可以调用父类中的实例函数classPerson{ name:string age:number gender:stringsayHi(food:string){console.log(`我是${this.name},喜欢${food}`)}constructor(name:string,age:number,gender:string){this.name = name this.age = age this.gender = gender }}classStudentextendsPerson{constructor( name:string, age:number, gender:string){// 使用super,调用父类的构造器实例化 super(name,age,gender){}// 调用父类中的方法 sayHi(food:string){super.sayHi(food)}}const p1 =newPerson("张三",25,"男")p1.sayHi('西瓜')// 我是张三,喜欢西瓜const s1 =newStudent('小明',18,'男)s1.sayHi('草莓')// 我是小明,喜欢草莓---

多态

  • 父类引用,指向子类对象
  • 不同类型的对象,针对相同的方法,产生了不同的行为
classAnimal{    
    name:stringconstructor(name:string){this.name = name           
    }run(distance:number){console.log(`${this,name}跑了${distance}米远的距离`)}}classDogextendsAnimal{constructor(name:string){super(name)}// 重写run函数    run(distance:number){console.log(`${this,name}跑了${distance}米远的距离-----dog`)}}classPigextendsAnimal{constructor(name:string){super(name)}// 重写run函数    run(distance:number){console.log(`${this,name}跑了${distance}米远的距离======pig`)}}const ani:Animal =newAnimal('动物')
ani.run()const dog:Animal =newDog('旺财')
dog.run()const pig:Animal =newPig('佩奇')
pig.run()// 使用多态const dog1:Animal =newDog('小狗')
dog1.run()const pig1:Animal =newDog('小猪')
pig1.run()

修饰符

用于描述类中的成员(属性、构造器、函数)的可访问性

  • public: 默认;表示公共的,被修饰的成员,任何位置都可访问;
  • private:表示私有的,被修饰的成员,仅类中可访问;子类的类中也不能访问(this.xxx);
  • protected:表示受保护的,被修饰的成员,子类的类中可访问(this.xxx);外部不可访问;

泛型

基本概念

泛型就是解决 类、接口、方法的复用性,以及对不特定数据类型的支持

  • 函数名称后加<>, 一般使用符号 T 占位, T代表具体的类型
  • 入参、出参的类型一般具有关联性
functionidentity<K>(...arg:T[]):T[]{return[...arg]}console.log(identity(1,2,3))

keyof关键字

keyof 用于获取某种类型的所有键,其返回类型是联合类型

代码释义:

表示只能是传入OOO类型, OOO类型中的属性之一(“id” | “code” | “age”)

let obj ={    
    id:"100",    
    code:996,    
    age:18}typeOOO=typeof objfunction main<T,P1extendskeyofT>(obj:T, prop1:P1){console.log(obj[prop1]);}main(obj,'id');// '100'main(obj,'code');// 996main(obj,'age');// 18

泛型约束

泛型可以表示任意类型, 但是实际编码中, 有时候可以知道类型大致的范围,

为了避免使用出错, 给泛型限定类型的使用范围, 这就是泛型约束(缩小类型的取值范围)

  • 一般通过两种方式实现 1. 指定更加具体的类型2. 添加约束

eg1: 限制出入参为数组, 数组元素类型不固定

functionfunc<T>(param:T[]):T[]{return param
}func([1,2,3,4])

eg2: 传入的类型, 必须含有 length 属性

interfaceILength{    
    length:number}functiongetId<Textends ILength>(param:T):T{console.log(param.length)return param
}// 虽然字符串和数组并没有继承 ILength, 但自身拥有length属性console.log(getId('农夫山泉'));console.log(getId([11,33,44]));

eg2: 传入的类型是一个对象, 必须拥有name, age属性

interfaceIUser{  
    name:string  
    age:number}functionfunc<Textends IUser>(param:T):T{console.log(param.name)console.log(param.age)return param
}

泛型在接口中的使用

interfaceIUser<T>{getId:(param:T)=>T}

泛型工具类

  • 泛型工具类型:TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作
  • 说明:它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。 这些工具类型有很多,主要学习以下几个:
  1. Partial<Type>
  2. Readonly<Type>
  3. Pick<Type, Keys>

Partial

  • Partial 用来构造(创建)一个类型,将 Type 的所有属性设置为可选。
typeProps={  
    id:string  
    children:number[]}typePartialProps= Partial<Props>
  • 解释:构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。

Readonly

  • Readonly 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。
typeProps={  
    id:string  
    children:number[]}typeReadonlyProps= Readonly<Props>
  • 解释:构造出来的新类型 ReadonlyProps 结构和 Props 相同,但所有属性都变为只读的。
let props: ReadonlyProps ={ 
    id:'1', 
    children:[]}// 错误演示
props.id ='2'
  • 当我们想重新给 id 属性赋值时,就会报错:无法分配到 “id” ,因为它是只读属性。

Pick

  • Pick<Type, Keys> 从 Type 中选择一组属性来构造新类型。
interfaceProps{  
    id:string  
    title:string  
    children:number[]}typePickProps= Pick<Props,'id'|'title'>
  • 解释: 1. Pick 工具类型有两个类型变量:1 表示选择谁的属性 2 表示选择哪几个属性。 2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可。2. 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。3. 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。

本文转载自: https://blog.csdn.net/weixin_46873254/article/details/122811368
版权归原作者 SpringSir 所有, 如有侵权,请联系我们删除。

“TypeScript学习笔记”的评论:

还没有评论