目录
5 . 对象类型 --- 接口 interface 继承 extends
TS的安装和运行:
ts安装 (查看版本号):
npm i -g typescript ( tsc -v)
ts-node 安装(可直接运行ts文件):
npm i -g ts-node (ts-node (文件路径)src/tsutil/01-hello.ts)
把当前ts环境变为模块化环境防止命名冲突 :
在当前文件中添加 export {} 即可,编译时可识别为模块化文件
ts-node运行ts报错时 :
先运行 tsc --init命令,初始化一下文件
TS相关介绍:
(一) ts类型兼容性
ts类型采用的是结构化类型系统(鸭子类型),根据结构识别是不是同一种类型,哪怕名字不一样
-
对象类型兼容性
( 如果两个对象的类型结构一样,那么两个对象可以相互赋值,如果两个对象的类型结构不完全一样,那么成员多的对象可以赋值给成员少的对象 )
成员多的可以赋值给成员少的,至少满足基本的要求,多了没事,把一个值赋给某个类型的时候,只要满足这个类型的基本需求就行,例如Point2D需要2个属性,p3有3个属性,p3满足了Point2D基本的要求,就可以对p2进行赋值,把p3赋值给p2,它会检查等号左侧要求的内容等号右侧有没有,只要满足有的话就可以
-
函数类型兼容性
( 考虑2个方面,参数个数 和 参数函数返回值 )
1. 函数参数个数类型:参数多的类型兼容参数少的(参数少的可以赋值给参数多的)
2. 函数返回值类型(和对象兼容性类似,只要满足的必须的类型要求就行,多了也可以)
例如下面类型不要求有返回值,但是有返回值也可以。
(二) ts.config.json文件介绍
(三) 类型声明文件介绍
(1) 类型声明文件作用
作用:用来为已存在的JS提供类型信息
- TS中有两种文件类型:
- .ts文件
- .d.ts文件
.ts文件:
1.既包含类型信息又可执行代码
2.可以被编译为js文件,然后,执行代码
3.用途:编写程序代码的地方
.d.ts文件:
1.只包含类型信息的类型声明文件
2.不会生成js文件,仅用于提供类型信息,在.dts文件中不允许出现可执行的代码,只用于提供类型
3.用途:为JS提供类型信息
总结:
- .ts是implementation(代码实现文件------文件中可以有类型又可以有可执行的代码逻辑)
- .d.ts是declaration(类型声明文件-----只包含类型信息的类型声明)
- 如果要为JS库提供类型信息,要使用,d,ts文件
(2) 类型声明文件分类:内置-第三方提供-自定义
2.1 内置的文件
---比如数组foreach方法、window/document都有对应的类型声明
2.2 第三方库提供的文件:分为2种
2.2.1 .库自带类型声明文件:比如,axios
- 查看node_modules/axios目录。
- 解释:这种情况下,正常导入该库,T$就会自动加载库自己的类型声明文件,以提供该库的类型声明。
- VSCode中如何找到它的类型声明文件?就是在import导入包的时候,会读取axios的package.json中的types字段,来加载指定的类型声明文作
2.2.2 .由DefinitelyTyped提供
- DefinitelyTyped是一个github仓库,用来提供高质量TypeScript类型声明,DefinitelyTyped 链接
- 可以通过npm/yam来下载该仓库提供的TS类型声明包,这些包的名称格式为:@types/*,比如,@types/react、.@types/lodash等。
- 在实际项目开发时,如果你使用的第三方库没有自带的声明文件,VSCod会给出明确的提示
import _ from 'lodash' //在VSCode中,鼠标移上去,查看'lodash'前面的提示
- TS 官方文档也提供了一个页面,可以来查询 @types/* 库 TypeScript: Search for typed packagests查找第三方提供的类型声明文件地址,下面提供这个地址的使用查找方法TypeScript: Search for typed packages
2.3 自定义的类型文件
在项目开发中,有两种常见情况,需要自己手动创建类型声明文件:
2.3.1 项目内共享类型 , 如果多个.ts文件中都用到同一个类型,此时可以创建.d.ts文件提供该类型,实现类型共享
操作步骤:
(1)创建index.d.ts类型声明文件
(2)在index.d.ts中创建需要共享的类型,并使用export导出(TS中的类型也可以使用import/export实现模块化功能)
类型声明文件index.d.ts export type NumStr = number | string
(3)在需要使用共享类型的.ts文件中,通过import导入即可(.d.ts后缀导入时,直接省路)
index.ts import {NumStr} from './index.d.ts' let b:NumStr = 1
2.3.2 .为已有JS文件提供类型声明
(1) 在将JS项目迁移到TS项目时,为了让已有的.js文件有类型声明,举例
(2) 成为库作者,创建库给其他人使用
(3) 类型声明文件的使用说明
- 说明1:TS项目中也可以使用.js文件
- 说明2:在导入.js文件时,TS会自动加载与js同名的.d.ts文件,以提供类型声明。
(1) 比如,在导入index.js时,会自动加载index.d.ts类型声明文件
- declare关键字:用于类型声明,为其他地方(比如,js文件)已存在的变量声明类型,而不是创建一个新的变量
(1)对于type、interface等这些明确就是TS类型的(只能在TS中使用的),可以省略declare关键字
(2)对于let、function等具有双重含义(在JS、TS中都能用),应该使用declare关键字,明确指定处用于类型声明
常用的 TS 类型定义:
在实际的编程过程中,原始数据类型的类型定义是可以省略的,因为 TypeScript 会根据声明变量时赋值的类型,自动推导变量类型,也就是可以跟平时写 JavaScript 一样:
一 原始数据类型
举几个例子
// 字符串类型
const str:string = 'Hello World'
// 数值类型
const num:number = 1
// 布尔值类型
const bool:boolean = true
// null 类型
const nullValue:null = null
//undefined类型
let undefinedValue:undefined = undefined
/E56中的Symbol类型
let uniqKey:symboI = Symbol()
二 数组类型
这种写法 let nums:Array<string>["1","2","3"], 与下面等义,简单了解即可
创建数值类型的数组
const numbers:number[] = [1,2,3]
创建字符串类型的数组
const strings:string[] = ["1","2","3"]
三 联合类型
比如:数组中既有number类型,又有string类型
let arr:(number | string)[] = [1,'a',3,'b']
再举一个例子,下面这个函数接收一个代表 “计数” 的入参,并拼接成一句话打印到控制台,因为最终打印出来的句子是字符串,所以参数没有必要非得是数值,传字符串也是可以的,所以就可以使用联合类型:
// 可以在 demo 里运行这段代码 function counter(count: number | string) { console.log(`The current count is: ${count}.`) } // 不论传数值还是字符串,都可以达到的目的 counter(1) // The current count is: 1. counter('2') // The current count is: 2.
四 类型别名
- 类型别名(自定义类型):为任意类型起别名
- 使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
- 使用 type 关键字来创建自定义类型
- 类型别名(比如,此处的CustomArray)可以是任意合法的变量名称
- 推荐使用大写字母开头
type CustomArray =(number | string)[] let arrl:CustomArray (1,'a'3,'b'l let arr2:CustomArray ['x','y',6,7]
五 函数类型
- 函数的类型实际上指的是:函数参数和返回值的类型
- 如果函数没有返回值,那么,函数返回值类型为:void
- 为函数指定类型的两种方式:
1 . 单独指定参数、返回值的类型
函数的返回值类型直接在函数小括号后添加 类型注解 即可
//函数声明
function add(numl:number,num2:number):number{
return numl+num2
}
//箭头函数
const add =(numl:number,num2:number):number =>{
return numl+num2
}
2 . 同时指定参数、返回值的类型
- 使用type关键字提前声明指定好函数参数、返回值的类型,这里使用箭头函数声明指定
- 这种函数指定方式的使用只适用于箭头函数表达式
type AddFn = (num1:number,num2:number)=>number
const add3:AddFn = (num,num2)=> {
return num1+num2
}
3 . 可选参数
- 这里的num1就是可选参数,num2就是必选参数,如果不指定那么函数参数默认是必选的
- 必选参数不能位于可选参数后
- 当参数有 默认值 时,该参数默认就是可选参数,不需?指定为可选了,如num2:number=1
const add =(num1:number,num2?:number):number =>{
return num1
}
add(3)
六 对象类型
- 对象类型的写法,对象类型实际上就是在描述对象的接口
- 推荐使用type来指定对象的类型
1 . 对象中 方法类型 的两种语法
- type做类型声明 --- 函数声明式的写法 sing(name:string): void
type typeobj2 = {
name: string
sing(name:string): void
}
- type做类型声明 --- 箭头函数式的写法 sing:(name:string)=>volid
type person = {
sing:(name:string)=>volid
}
let obj2: { name: string; sing(): void } = {
name: "ls",
sing() { }
}
也可以同等于下面这种写法,用type先进行 类型别名 的声明再使用
type typeobj2 = {
name: string
sing(): void
}
let obj2: typeobj2 = {
name: "ls",
sing() { }
}
如果方法有参数,就在方法名后面的小括号中指定参数类型
type typeobj2 = {
name: string
sing(name:string): void
}
let obj2: typeobj2 = {
name: "ls",
sing(name) { }
}
// 箭头函数形式的方法类型
type person = {
sing:(name:string)=>volid
}
let person:person = {
sing(name){}
}
2 . 对象类型 --- 可选属性
对象的属性默认也是必选的,在属性后面加上问号"?"就是可选属性,同函数类型的可选参数一致
比如我们封装api的函数,需要传递一些参数,有的参数需要传,有的不需要,
比如axios的get请求时,就可以省略methed属性,因为axios默认就是get请求
type Config = {
url: string,
method?: string,
}
const myAxios = (config:Config)=>{}
myAxios({
url:"", // method这里是可选就可以不写
})
3 . 对象类型 --- 接口
- 可以使用 interface 关键字来为对象指定类型
- 使用interface声明类型别名时,类型别名一般约定为大写I开头
- interface 的使用方法和 type 基本一样
4 . interface 和 type 区别
interface使用场景没有type多,一般尽量都使用type
相同点
- 都可以给对象指定类型
不同点
- 接口 interface 只能为对象 指定类型
- type 可以为任意类型指定 类型
5 . 对象类型 --- 接口 interface 继承 extends
- 接口继承的意思就是,如果两个接口之间有相同的属性和方法,可以将公共的属性或方法抽离出来,通过继承关键字 extends 来实现复用
- 这里要注意使用type 这个自定义类型的关键字 声明的对象类型是没有办法继承的,只有interface才可以。
6 . 使用交叉类型来实现 type 继承的功能
- 使用 &符 进行类型的交叉使用
- 后声明的类型写在&符后的{}里
七 元组类型
使用场景:
- 在地图中,使用经纬度坐标来标记位置信息
- 可以使用数值数组number[]来记录坐标但是不严谨,因为该类型的数组中可以出现多项数值.
- 更好的方式,使用元组类型,元组 :Tuple
元组 :Tuple
- 元组类型是另一种类型的数组
- 它确切的标记出有多少个元素,以及特定索引对应的类型
- react中的useState hook的返回值类型就是一个元组类型,因为对于useState来说,它的返回值长度固定且每个索引对应的元素类型也是知道的
使用元组来表示经纬度信息写法:
let pos:[number,number] = [116.6546, 863.5434]
八 类型推论
在ts中,某些没有明确指出类型的地方,ts的类型推论机制会帮助提供类型
由于类型推论的存在,某些场景的类型注解可以省略不写(函数参数类型一定要写)
- 第一种场景常见:声明变量并且初始化时,比如 let age = 10
- 第二种场景常见:决定函数返回值时,比如:
//这里函数的返回值类型就可以省略不写,自动推断为number类型 const add =(numl:number,num2:number)=>{ return numl+num2 }
- 第三中场景:绑定事件时:可以自动推断中事件对象的类型,比如:
// e 参数自动分配了UIEvent类型,这个类型是window专属的 // 事件中的事件对象可以自动推断 window.addEventlistener('resize',(e)=>{})
在编辑器中写代码时,多看方法属性的类型,养成写代码看类型的习惯
九 字面量类型
字面量类型指的就是一个特定的值,一种值就是一种字面量类型,之前我们理解string,number是一种类型,字面量也是一种类型,字面量意思就是字面上能看出来这是个什么,你看到的一个值就是可以当做一个类型,比如对象数字等都可以当做一种类型,比如用const声明的变量,这个变量的类型就是变量的值,因为const本身就是声明常量的关键字它的值不能被改变。
- 使用模式:字面量类型 配合 联合类型 一起使用
- ,场景:用来表示一组明确的可选值列表,如我明确知道要选用的值,就可以声明一组和这个值相关的字面量类型。
比如贪吃蛇游戏 方向可选值 只能是上下左右 type Direction = 'up' | 'down' | 'left' | 'right' function changeDirection (direction: Direction){} changeDirection ('up')
十 枚举类型 (了解)
- 类似于字面量类型加联合类型组合的功能,也可以表示一组明确的可选值
- 定义一组命名常量,它描述一个值,改值可以是这些命名常量中的一个
- 枚举既可以作为一个类型,在调用的时候能看到枚举的类型也可以作为一个值出现
1 . 数字枚举
- 枚举成员是有值的,默认为是:从 0 开始的自增的数值
- 我们把枚举成员的值为数字的枚举,称为 数字枚举
- 也可以给枚举中的成员赋任意初始化值
//指定了一个值后面值自增 down=11,left=12,right=13 enum Direction {up = 10,down,left,right} // 自定义值也可以 enum Direction {up = 5,down = 8, left =17,right = 90}
2 . 字符串枚举
- 枚举成员值是字符串
- 字符串没有自增长行为,因为,字符串枚举的每个成员必须有初始值
3 . 枚举类型实现原理
十一 any类型
十二 类型断言
- 使用场景:ts类型推断不准确的时候就可以使用类型断言
- 语法:as 更加具体的类型
- 如何获取标签的类型呢
十三 typeof 运算符
- TS也提供了typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)
- 使用场景:根据已有变量的值,获取该值的类型,来简化类型书写
- 1.使用 typeof 操作符来获取变量p的类型,结果与第一种(对象字面量形式的类型)相同
- 2. typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就是类型上下文(区别于JS代码)
- 3. 注意:typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函敬调用的类型)
十四 泛型类型(高级类型-重点)
1 . 泛型函数的创建和调用
泛型可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中
创建泛型函数:
function id<Type>(value:Type):Type return value
//也可以仅使用一个字母来作为类型变量的名称
function id<T>(value:T):T return value
调用泛型函数: 可传入任意类型,达到不同的类型一起工作。
const num = id<number>(10)
const str= id<string>('a')
解释:
- 语法:在函数名称的后面添加<>(尖括号),尖括号中添加类型变量,比如此处的Type
- 类型变量Type,是一种特殊类型的变量,它处理类型而不是值
- 类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)
- 因为Type是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型
- 类型变量Type,可以是任意合法的变量名称
2 . 简化泛型函数调用
const num = id(10)
const str= id('a')
解释:
- 此时,TS内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动推断出类型变量Type的类型
- 比如,传入实参10,TS会自动推断出变量num的类型number,并作为Type的类型
- 推荐:使用这种简化的方式调用泛型函数,使代码更短,更易于阅读
- 说明:当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数
3 . 泛型约束
默认情况下,泛型函数的类型变量Tye可以代表任意类型,这导致无法访问任何属性,所以需要泛型约束
此时,就需要为泛型添加约束来收缩类型(缩窄类型取值范围)
添加泛型约束收缩类型有两种方式:
(1)指定更加具体的类型
(2)添加泛型约束:使用extends关键字来添加类型约束
比如举个例子
- 创建描述约束的接口ILength,该接口要求提供length属性
- 通过extends关键字来为泛型(类型变量)添加约束
- 该约束表示:传入的类型必须具有length属性
4 . 多个类型变量的泛型
typeof ( 取到变量在ts中的类型 )
keyof ( 获取到对象Obj了中所有的属性,最终,会得到一个对象0bj中所有属性名称的联合类型 )
5 . 泛型接口(了解)
6 . 泛型工具类型
主要了解:
1.Partial<Type>
2.Readonly<Type>
3.Pick<Type,Keys>
Partial: Partial<Type>用来构造(创建)一个类型,将Type的所有属性设置为可选
type Props = {
id:string
children:number[]
}
type PartialProps = Partial<Props>
构造出来的新类型PartialProps结构和Props相同,但所有属性都变为可选的.
Omit: Partial<Type>可以剔除 已定义对象中 自己不需要的一部分形成新的定义类型。
type Props = {
id:string
age:20
name:'lisi'
}
type PartialProps = Omit<Props,'age'>
构造出来的新类型PartialProps结构不包含age属性,包含id和name属性
Readonly: Readonly<Type>用来构造(创建)一个类型,将Type的所有属性设置为只读
type Props = {
id:string
children:number[]
}
type ReadonlyProps = Readonly<Props>
构造出来的新类型ReadonlyProps 结构和Props相同,但所有属性都变为只读的.
错误演示
let props:ReadonlyProps ={id:'1',children:[]}
//会报错,错误演示,只读是无法修改的
props.id ='2'
Pick: Pick<Type,Keys> 从Type中选择一组属性来构造新类型,从一个对象类型中取出想要的几个属性,要几个取几个构成一个新的类型,举例:
interface Props {
id:string,
title:string,
children:number[]
}
type PickProps = Pick<Props,'id'| 'title'>
// PickProps类型中 就只有两个属性了
- Pck工具类型有两个类型变量:1表示选择谁的属性2表示选择哪几个属性。
- 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
- 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
- 构造出来的新类型PickProps,只有id和title两个属性类型。