目录
类型注解
一种为变量添加类型约束的形式,(什么类型的变量,赋值什么类型的数据,否则会报错)
语法类型:var/let/const 变量名:数据类型=值
number和Number的区别?
虽说在声明的时候,并不会进行报错,但是在设置数据类型注解的时候,尽量使用小写,大部分基本数据类型都是小写的,Number一般代表的是一个类
ts中的变量的声明,注意变量名是否重复
在ts中使用let声明的变量,会在全局的文件中进行查找,如果有重复的,该变量的声明就会报错(有波浪线),所以在ts文件中底部 export {} 形成一个块级作用域
什么时候进行类型注解?
默认情况下 ts会帮我们将初始赋值的数据类型作为当前变量的类型
当只定义一个变量,没有初始值的时候,给变量进行类型注解,避免重复赋值不同类型的数据
let k;
k=1
k="1"
//应为
let k:string
k="12"
类型注解有哪几种?
1.number
2进制 0b1010 以0b开头的
8进制 0o744 以0o开头
10进制
16进制 0xfood 以0x开头
2.boolean
只有两个值 true和false
3.null和undefined
在ts中,null和undefined即是数据类型,也是他们所对应的值 将变量null赋值null,保持一致 null和undefined是所有类型的子类型 eg:let num:number=undefined //不会报错
4.symbol
声明的变量不能重复,是唯一的
let name1:symbol=Symbol()
[name1]='1213'
5.Array
第一种:(推荐使用) let arr:元素类型[]=['2','3','5']; 第二种:泛型的写法 let arr:Array<元素类型>=[] 第三种 let arr:元素类型[]=new Array('','','')
6.any
变量的值,可以是任何类型的(和原来的js完全一样)
变量在声明的时候,未指定类型的话,就会被认为是任意值类型
let name:any='122'
name=12
name=true
//可以在上面访问任意的属性或调用任何方法
name.first
7.unknown
只能赋值给any和unknow类型的变量
let res: unknown
let msg1:unknown=res
8.function
函数的形参和实参
function 函数名(形参名:数据类型){
代码逻辑
}
函数名(实参)
//形参和实参 一一对应,类型也要对应
函数返回值
<1.>将函数内部计算的结果进行返回,以便使用该结果进行其他运算
在函数名()后面写 :返回值的数据类型
eg:function 函数名(): number{
return 15
}
//若没有指定函数的返回值,那么函数的返回值的默认类型为void(空,啥也没有)
1.当用变量接收函数返回值时,变量的类型和返回值的类型一致
2.直接使用函数的返回值进行其他运算
3.`在开发的过程中,一般不用去写函数返回值的类型注解(自动推导)`
<2.>终止代码执行,return后的代码不会被执行
<3.>return只能在函数内使用
<4.>可以单独使用,后面可以不跟内容,用来可以终止函数执行
<5.>箭头函数或匿名函数中的item的数据类型可以根据上下文环境推导出来,可以不进行添加
<6.>可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了;
TypeScript 会将添加了默认值的参数识别为可选参数,此时就不受前后位置的限制了
//设置默认值在确定参数前,
function fn2(num:number = 100, y:number){
return num+y
}
fn2(undefined,200) //要传两个参,否则会报错
fn2(null,200)
//
//设置的默认值在确定参数后,可以只传一个确定参数即可
<7.>剩余参数
使用ES6中的...,将函数的参数进行接收,并进行类型注解
function fn(...nums:number[]){
console.log(nums)
}
fn(1,2,3,4,5)
<8.>可推导的this类型
let obj={
fn(){}
}
obj.fn() //此时的this指的是obj
<9.>不确定的this类型
fucntion fn(){}
let obj={
fn,
}
obj.fn() //ts推导得来,此时的this指的是obj
<10.>确定的this指向
//给函数的第一个参数传入this,告诉ts,此时函数中的this指向的是谁
type obj={
name:string,
}
function hrl(this: obj,num:number) {//第二参数,进行类型注释,否则报错
console.log(this.name,num);
}
let obj5={
name:'lalal',
hrl,
}
obj5.hrl(123) //this指的是obj5
<11.>函数的重载
函数签名:通过函数签名的形式实现函数的重载
注意:有重载的情况下,即使数据类型是any,只要不符合任意一个重载函数别名,就不能使用重载
function sum(a1:number, a2:number): number;
function sum(a1:string, a2:string): string;
function sum(a1: any, a2: any){
return a1+a2
}
//在调用sum函数的时候,它会根据我们传入的参数类型,来决定执行函数时,到底执行哪一个函数签名
sum(1,2);//3
sum("1","2");//"12"
9.void
函数无返回值的时候,用:void代表undefined或null
10.never
用于表示永远不会返回值的类型;不要把一个变量赋值给msg:never类型的变量,否则这个变量永远获取不到
function f(): never {
while (true) {
console.log(111);
}
}
11.tuple元组
多种元素的组合
必须要进行赋值
function createState(state: any) {
let curState = state;
let setState = (newValue: any) => {
curState = newValue
}
let arr: [any, (newValue: any) =>void] = [curState, setState]
return arr
}
let [curState, setState] = createState(100);
setState(101);
console.log(curState)
//[]中的类型要和赋的值一一对应
//优化的写法:使用T替换any
数组中的数据类型应该保持统一,如果不统一的话,使用对象进行代替
12.enum枚举
将一组可能出现的值, 一个一个列举出来, 定义在一个类型中,这个类型就是枚举类型
枚举类型放常量 字符串 数字 (全大写)
enum Direction {
LEFT = "LEFT", //值可以是多种类型的
RIGHT = "11",
TOP = "1111",
BOTTOM = 100
}
场景:当变量的值,只能是几个固定值中的一个,应使用枚举来实现
类型推论
type Inference,ts会在没有明确指定类型的时候推测出一个类型,这就是所谓的类型推论
let num=1
num="ldl" //在编译的时候,会进行报错
//
let num:number=1
num='ldl'
//声明变量并初始化时;函数返回值时,这两种情况类型注解可以省略
类型的补充
每个属性的类型也是可以指定的 ,如果不指定那么就是any类型;
flag?:boolean 加问号表示可以传递也可以不传递
1.联合类型注解
union Types表示取值可以是多种类型中的一种,类型之间以|隔开
语法::number | string
function getID(id: number|string|boolean){
//narrow 在ts中每当使用数据类型判断的时候 ts会自动的缩小 范围
if(typeof id === "string"){
//ts帮助确定id一定是string类型
console.log(id.toUpperCase())
}else {
console.log(id)
}
}
getID("abc")
getID(123)
//
//注意:访问类型中的属性,如length,一定要是类型所具有的,如果没有就会报错,但是使用整个联合类型.length就不会报错
2.可选类型
可以看作undefined和所写类型的一个联合类型
eg: msg?:string 等同于 msg:undefined|string
3.类型别名
语法:type x=number|string
使用type将一个变量的类型注解提取出来 //对象的类型别名 type Data = { name: string, age: number, flag?: boolean } let data: Data = {name:"1", age:2, flag:true};
4.类型断言
在一些时候ts无法获取具体的类型信息(HTMLElement),但是这些(HTMLImageElement)信息有又独特的属性和方法,此时就需要告诉ts当前的这个数据类型到底是那种
TS只允许类型断言转换为 更具体 或者 不太具体(any/unKnown) 的类型版本. 这种情况会导致ts类型混乱
let test = document.getElementById("test") as HTMLImageElement test.src = "图片地址url" //不写as,会报错
ts不允许把一个具体的数据类型断言成另一个具体的数据类型,而是需要首先把这个数据类型断言成一个unknown或any这种不具体的,再把不具体的类型断言成另一个具体的数据类型
let str: string = "你好世界"; let num: number = str as unknown as number
5.非空类型断言
当我们在编写代码的时候, 在执行ts编译阶段会报错
非空断言 使用的!, 表示可以确定某个标识(变量)是有值的, 可以跳过ts在编译阶段对它的检测
确定我们传入的参数有有值的, 这个时候就可以使用非空类型断言
function fn(msg?:string){
//传入的msg有可能是为undefined的, 这个时候是不能执行uppercase方法
console.log(msg!.toUpperCase())
}
fn("hello")
6.可选链的使用
可选链 ES11(2020年) 中新增的特性 不是TS中独有的
当去读取一个对象的属性,但是这个属性不存在的时候(就会返回undefined),继续通过.的形式访问下一级(undefined.属性),就会出现报错
type Person = {
name: string,
friend?: {
name: string,
age?: number,
girlFriend?: {
name: string
}
}
}
console.log(obj.friend?.name);
console.log(obj.friend?.girlFriend?.name);
拓展: !! ??
!!用于转换数据类型
??空值合并操作符
let res=null
let msg=res??'hrl' //有值的话hrl,没的话null
7.字面量类型
字面量类型 只能赋值 字面量
let str:'a'='a'
字面量结合联合类型:为了确保当前变量不会被其他一些非相关的值覆盖
type Color = "red" | "green"
let color: Color = "red"
color = "green"
//只能是这两个值
8.类型缩小
type narrowing
使用联合类型注解结合typeof检测数据类型中是否具有某个方法
使用typeof x="number"判断语句,缩小比声明时更小的类型,改变ts的执行路径(不再去检验除number之外的其他类型)
常见的类型保护:typeof instanceof
常见类型比较:=== == !== != switch case
js中的in运算符:来确定当前的对象是否有该属性;若执行的属性存在这个对象或对象的原型链中,返回的都true
type obj{
name:string
}
name in obj //true
Object.prototype.age=12
age in obj //true
ts中的in:用于判断某个属性是否存在于指定类型中
type Cat = {
name: string;
run: ()=>void;
}
type Fish = {
name: string;
swim: ()=>void;
}
function move(animal: Cat | Fish) {
if("run" in animal){
animal.run()
}else {
animal.swim()
}
}
let cat:Cat = {
name: "小猫",
run(){}
}
let fish:Fish = {
name: "小鱼",
swim(){}
}
move(cat)
9.交叉类型
交叉类型(intersection Types)合并,表示需要满足多个条件,使用&符隔开;表示的含义是需要同时满足配置的数据类型,不可能有这样的数据,所以可以将看做为never
type obj=number & string
ts中的类
ts中的类,类中的变量,必须单独的拿出来进行类型注释,否则会报错
class Person{
name!:string;
age:number;
constructor(name:string, age:number) {
this.name = name;
this.age = age;
}
running(){
console.log(this.name + " running")
}
}
let p = new Person("ES6+",7)
//类的类型
let p1:Person={
name:'kakk',
age:115,
running(){}, //要和类的结构保持一致,否欧会报错
}
1.类的继承
2.类的修饰符
类的属性和方法支持三种修饰符
public
修饰的是在任何地方可见 公有的属性或方法 默认编写的属性就是public的
private
修饰的是仅在同一类中可见 私有的属性或方法(不参与继承)
只能在类的创建中访问被private修饰的属性
protected
修饰的是仅在类自身及子类中可见;受保护的属性或方法(不能读写)
在子类中,使用super.方法的形式进行调用;属性仍不能被访问
readonly
修饰的是这个属性我们不希望外界可以任意的修改,只希望确定值之后直接使用(只能读)
3.类中的setters和getters
在一些时候,我们不希望直接去修改类中的私有属性,或者我们想去监听当前这个属性被读写的过程
4.类中的静态成员
我们在类中定义的属性和方法都属于是通过实例才能访问或者使用的(对象级别)
有时候我们也需要定义类级别的属性和方法;在ts中通过关键字static来定义;
定义后的变量只能通过类.属性/方法的形式进行访问
class A{ static name:string; } let a=new A('来电铃声了') A.name //而不是a.name
5.抽象类
抽象类的特性
-
抽象类不能使用new关键字,不能被实例
-
抽象方法必须被子类继承多实现,就是说,对于这个父类中的方法,子类的声明里面,使用该方法;
-
子类必须覆盖抽象类的抽象方法,也就是一定要写这个方法
-
一旦有抽象方法,就一定要把这个类声明为抽象类
抽象类和抽象方法
接口
interfaces 接口一般首字母大写,ts中的接口,除了对类的一部分行为进行抽象外,页面用于对对象进行描述
接口的结构的情况
1.使用了定义的接口的变量必须和接口的结构保持一致;也就是不能少也不能多属性
2.若不想完全匹配,就是用到了可选属性
interface Person { name: string; age?: number; } let tom: Person = { name: 'Tom', age: 25 //可写可不写 };
3.变量中,新增任意属性,但接口中并不存在
interface Person { name: string; age?: number; [propName: string]: any;//或string|number } let tom: Person = { name: 'Tom', gender: 'male' }; //注意:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集: //就是说,any的位置,必须要有确定属性的类型注解 string和number,没的话就会报错; //所以,使用any或者 **联合类型注解**
4.只读属性
就是说这个属性一旦被初始化,就不能在发生改变;
在接口中使用readonly定义只读属性
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let tom: Person = { //要给id进行初始化赋值 name: 'Tom', gender: 'male' }; tom.id = 89757;//只读属性,不能再次赋值
使用接口定义类数组
function sum() {
let args: IArguments = arguments;
}
//
//实际上IArguments是ts定义好的
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
//除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有 length 和 callee 两个属性
使用接口定义函数
使用接口定义函数的话,使其复杂化,推荐使用type定义函数的类型
interface IFn { (num1:number, num2:number):number //看不懂 } let fn:IFn = (num1,num2)=>{ return num1+num2 }
接口的继承
接口支持多个继承(而类只能继承一个)
interface Obj {
name: string,
run: () => void
}
interface Obj2 {
age: number
}
//接口是支持多继承的(类的继承只能继承一个)
interface Obj12 extends Obj, Obj2 {
jump: () => void
}
let obj: Obj12 = {
name: "你好",
age: 18,
run(){},
jump(){}
}
接口和type的区别?
-
在开发中,对于对象类的数据,建议使用interface进行声明
-
针对非对象型的数据,推荐使用type进行声明
-
interface可以重复进行声明,type不能,type定义的别名是不能重复的
抽象类和接口的区别?
接口中的字面量赋值
在ts通过字面量直接赋值的过程中, 为了进行类型推导会进行严格的类型限制
但是我们之后将一个 变量 赋值给其他变量的时候, ts会进行一个(freshness)擦除操作
interface Person{ name: string, age: number } let obj = { name: "你好", age: 18, flag: true } let p:Person = obj; //obj中必须包含接口中的类型数据,多余的属性,会被擦除
泛型
泛型函数
在开发过程中,一些时候我们声明的函数不仅仅明确它的数据类型,而且还要让这个方法有很强的可复用性
实现类型参数化------泛型
//函数名的后面,通过<形参>的形式,接收传递过来的参数 fucntion fn<T>(arg:T){ return arg } //调用者以参数的形式,告诉我们函数的参数是一个什么类型 fn<string>(arg:'121') 1.通过<类型>的方式将类型传给参数 2.通过类型推导自动的推导出我们传入的变量的类型;在类型推导中我们传入的变量相当于是字面量类型,它对函数也是适用的
不使用any的原因:any的强度较高,在ts中用来定义参数类型的话,会丢失一些信息
泛型接口
interface I<T>{ name:T } let obj:I<string>={ name:'dmksm' }
泛型类
class A<T>{ name:T } let a=new A<string>('d')
泛型约束
没有length的数据要想实现获取到长度的需求?
1.借助子继承的方式,使这个类就有length属性
2.或是直接在传入的参数中添加length属性
k extends keyof T
代表: k必须是来自T的属性的集合中的一个元素
拓展知识
模块化开发
导出: export {} 导入: import {} from '文件路径' 导出: export default{} 导入: import 变量 from '文件路径'
命名空间
namespace:借助该关键字,可以实现重复声明
命名空间的主要目的:就是将一个模块内部再进行作用域的划分,防止命名冲突
export namespace Time{ export function formate(time:string){ return '处理结果' } }
第三方库的.d.ts文件
.d.ts文件的下载地址http:// https://www.typescriptlang.org/dt/search?search=?search=
.d.ts文件使用来作类型声明(declare)的,它仅仅用来做类型检测的;一般安装第三方插件的话,会带有这个文件,如果没有还想使用该插件的话,就需要去官网去下载