对象类型:object 包括数组 对象 函数等
数组类型两种写法
let numbers:number[]=[1,2,3]
let numbers:Array<number> =[1,2,3]
联合类型 自定义类型(类型别名) 接口 元组 字面量类型 枚举 void any等
推荐第一种简单明了
如果既有number类型又有string类型 要加()
let arr:(number|string)[]=[1,'2']
没有小括号
let arr1:number|string[]=[a,b]
let arr1:number|string[]=123
类型别名:为任意类型起别名
使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
1.使用type关键字创建类型别名
2.创建类型别名后直接使用还类型别名作为变量的类型注解即可
给函数加类型就是给函数的参数和返回值加类型
1.单独指定参数和返回值类型
function add(number1:number):number {
return 111
}
const add = (num1:number):number=>{
return 111
}
当函数作为表达式的时候可以通过类似箭头函数的语法为函数添加类型
这种形式只适用于函数表达式
const add:(num1:number)=>number=(num1)=>{
return 111
}
如果函数没有返回值 返回的类型为void
function one(num:number):void {
console.log('没有返回值')
}
?表示可选参数
可选参数只能出现在最后面,也就是说要是必选参数的话必须写在最前面
function one(num2:number,num?:number ):void {
console.log('没有返回值?表示可选参数')
}
1.属性名采用
属性名:类型的形式
方法采用方法名():返回值类型的形式
2.在一行代码中指定对象的多个属性类型时,使用;分隔,如果一行代码只指定一个属性类型 可以去掉;
方法的类型也可以使用箭头函数的形式(比如:{sayHello:()=>void})
可选属性用?表示
let person: {
name: string;
age: number;
sayHello(): void
}
= {
name: '你好',
age: 111,
sayHello(){
console.log('你好')
}
}
当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的
解释:
1.使用interface关键字类声明接口
2.接口名称可以是任意合法的变量名称
3.声明接口后,直接使用接口名称作为变量类型
4.因为每一行只有一个属性类型,因此属性类型后没有;(分号)
interface Iperson {
name: string
age: number
sayHi():void
}
type Iperson = {
name: string
age: number
sayHi():void
}
let person: Iperson = {
name: 'nnan',
age: 11,
sayHi() {}
}
interface(接口)和type(类型别名)的对比
相同点:都可以给对象指定类型
不同点:
接口,只能为对象指定类型
类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名
如果两个接口之间有相同的属性或者方法,可以讲公共的属性或方法抽离出来,通过继承来实现复用。比如,这两个接口都有x,y两个属性,重复写两次,可以,但是很繁琐
interface Point2D {
x:number;
y:number
}
interface Point3D extends Point2D {
z:number
}
解释:
1.使用extends(继承)关键字实现了接口Point3D继承Point2D
2.继承后,Point3D就有了Point2D的所有属性和方法(此时,Point3D同时有x,y,z三个属性)
场景:在地图中,使用经纬度坐标来标记位置信息
可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型
使用number[]的缺点:不严谨,因为改类型的数组中可以出现任意多个数字。
更好的方式:元组(Tuple)
元组类型是另一种类型的数组,它确切地包含多少元素,以及特定索引的对应的类型
解释:
1、元组类型可以确切地标记出有多少个元素,以及每个元素的类型。
2、该示例中,元素有两个元素,每个元素的类型都是number
const point: [number, number] = [120, 10]
TS类型推论机制会帮助提供类型
1、声明变量并初始化时 2、决定函数返回值时
注意:这两种情况下,类型注解可以省略不写
推荐:能省略类型注解的地方就省略
技巧:如果不知道类型,可以通过鼠标放在变量名称上,利用VSCode的提示来查看类型。
解释
1.使用as关键字实现类型断言
2.关键字as后面类型是一个更加具体的类型
技巧:如果不知道DOM元素的类型可以通过console.dir()打印DOM元素,在属性列表的最后面,既可看到该元素的类型
何时用类型断言 当明确知道类型的时候
let str1 = ‘111’
const str2 = ‘你好’
str1的类型是string str2的类型是’你好’
解释:
1.str1是一个变量(let)它的值可以是任意字符串,所以类型是string
2.str2是一个常量(const)它的值不能变化 所以是‘你好’
注意:此处的你好,就是一个字面量类型,也就是说某个特定的字符串也可以作为TS中的类型。除字符串外,任意的JS字面量(比如,对象、数字等)都可以作为类型使用
优势:相比于string类型,使用字面量类型更加精确严谨
适合一组明确可选值的情况
function changeNumber(number:1|2|4) {
}
changeNumber(1)
枚举的功能类似于字面量+联合类型组合的功能,也可以表示一组明确的可选值
枚举:定义一组命名常量
解释
1、使用enum关键字定义枚举
2、约定枚举名称、枚举中的值以大写字母开头
3、枚举中多个值以逗号分割
4、定义好枚举后,直接使用枚举名称作为类型注解
enum Direction {
up,
down,
left
}
function changeDirection (direction:Direction) {
}
changeDirection(Direction.down)
注意形参direction的类型为枚举Direction 那么实现的值就应该是枚举Direction成员的任意一个
解释:类似于JS中的对象,直接通过点(.)语法访问枚举的成员
枚举成员是有值的 默认为:从0开始自增的数值
我们把枚举成员值为数字的枚举,称为:数字枚举
也可以根据需求给枚举中成员初始值
字符串枚举:枚举成员的值是字符串
字符串枚举必须有初始值
enum Direction {
up='up',
down='down',
left='left'
}
function changeDirection (direction:Direction) {
}
changeDirection(Direction.down)
枚举是TS中为数不多的非js类型及扩展的特性之一
因为:其他类型仅仅被当做类型,而枚举不仅用作类型,还能提供值(枚举成员都是有值的)
也就是说其他类型会在编译为js代码时自动移除,但是枚举类型会被编译为js代码
一般情况下推荐字面量类型+联合类型的方式 这种方式更加的简洁高效
尽可能避免使用any类型,除非临时使用
隐式具有any类型情况
声明变量不提供类型也不提供您初始值
声明一个函数不给函数的参数添加类型
js中提供typeof操作符,可以在类型上下文中引用变量或者属性的类型(类型查询) 使用场景:根据已有变量的值,获取该值的类型,简化书写
ts也提供了typeof操作符:可以在类型上下文中引用变量或者属性的类型(类型查询)
注意typeof只能用来查询变量或者属性的类型,无法查询其他形式的类型(比如,函数调用的类型)
const p = {x:1,y:1}
function formatPoint (point: typeof p) {
}
formatPoint({x:1,y:2})
1、class类
2、类型兼容性
3、交叉类型
4、泛型和keyof
5、索引签名类型和索引查询类型
6、映射类型(可以实现根据已有类型生成现有类型 )
TS中的class,不仅提供了class的语法功能,也作为一种类型存在
实例属性初始化
构造函数
class Person {
age: number
name: string
constructor(age:number, name:string){
this.age=age
this.name=name
}
}
解释: 1、成员初始化(比如,age:number)后,才可以通过this.age来访问实例成员
2、需要起构造函数指定类型注解,否则会被隐式推断为any 构造函数不需要返回值类型
方法的类型注解(参数和返回值)与函数用法相同
cpp
class Person {
age =1
ageAdd(n:number):void {
this.age+=n
}
}
const one = new Person()
one.ageAdd(333)
console.log(one.age)
类的继承两种方式
1、extends(继承父类)
2、implements(实现接口)
继承是两个类之间的关系 实现是接口和类之间的关系
interface Singable {
name: string
sing():void
}
class Person implements Singable {
name= 'hha'
sing() {
console.log(this.name)
}
}
const one = new Person()
one.sing()
1、private
可以使用TS来控制class方法或属性对于class外的代码是否可见
可见修饰符包括1、public(公有的)2、private(私有的) 1、公有成员可以被任何地方访问
解释 1、在类属性或方法前面添加public关键字,来修饰该属性或方法是共有的
2、因为public是默认可见性,所以可以直接忽略
类的继承
class Animal {
move(){
console.log('移动')
}
}
class Dog extends Animal {
moveDog() {
this.move()
}
}
const dogOne = new Dog()
dogOne.moveDog()
2、protected
解释 1、在类属性或者方法中添加protected关键字,来修饰该属性或者方法是受保护的
2、在子类的方法内部可以通过this来访问父类中受保护的成员,但是,对实例对象不可见
3、private
表示私有的,只有在当前类中可见,对实例对象以及子类也是不可见的
解释 1、在类属性或方法前面添加private关键字,来修饰属性或方法是私有的 2、私有的属性或者方法只有在当前类中可见
表示只读,用来防止在构造函数之外进行赋值 1、readonly关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法
2、接口或者{}表示的对象类型,也可以用readonly 注意只要是ereadonly来修饰的属性 必须手动提供明确的类型
interface Iperson {
readonly name: string
}
let obj:Iperson ={
name:'你好'
}
obj.name='lll'
两种类型系统:
结构化类型系统 Structural Type System 标明类型系统 Nominal Type System
ts是结构化类型系统 类型检查关注的是类具有的形状
如果两个对象具有相同的形状,则认为他们属于同一个类型
例如
class Point{
x:number;
y:number;
}
class PointOne{
x:number;
y:number;
z:number
}
const one: Point= new PointOne()
解释 1、Point和PointOne是两个不同的类
2、one是PointOne的实力对象但是因为ts是结构化类型视同只检查Point和PointOne接口是否相同(是否有相同的属性和属性类型)
3、如果是Nominal TypeSystem,中(例如 C JAVA)他们是不同的类就无法兼容
类和接口之间是可以兼容的
class PointOne{
x:number;
y:number;
z:number
}
interface PointTwo {
x:number;
y:number;
}
const one: PointTwo= new PointOne()
函数之间的兼容性
需要考虑
1、参数个数
2、参数类型
3、返回值类型
参数个数
解释
参数少的可以赋值给参数多的
数组forEach方法的第一个参数是回调函数 在JS中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性
并且因为回调函数是有类型的 所以TS会自动退到处参数item index array
参数类型 相同位置的参数类型要相同 参数少的可以赋值给参数多的
返回值类型
解释 1、如果返回值类型是原始类型,此时两个类型要相同
2、如果返回值是对象类型,此时成员多的可以赋值给成员少的
type F7 =()=>{name:string}
type F8 =()=>{name:string,age:number}
let f7:F7
let f8:F8
f7=f8
交叉类型(&):功能类似于接口继承 用于组合多个类型为一个类型(常用于对象类型)
interface contact1 {
name: string
}
interface contact2 {
age: number
}
type contact = contact1&contact2
let obj:contact ={
name:'你好',
age: 999
}
相同点:都可以实现对象类型组合 不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同
接口类型继承同名属性会冲突 交叉类型会变成联合的|
以上总结 用交叉类型就好了
泛型是可以在保证类型安全的前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中
泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同类型一起工作,灵活可复用
创建泛型函数 解释: 1、语法:在函数名称的后面添加<>(尖括号),尖括号中添加类型变量,例如T
2、类型变量T,是一种特殊类型的变量,它处理类型而不是值
3、改类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)
4、因为T是类型,因此可以将其作为函数参数和返回值类型,表示参数和返回值具有相同的类型 5、类型变量T,可以是任意合法的变量名称
解释: 1、在调用泛型函数时,可以省略<类型>来简化泛型函数的调用
2、此时,TS内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动推断出类型变量Type类型
推荐:使用这种简化的方式调用泛型函数,使代码更短。更易阅读 说明:当编译器无法推断类型或者推断类型不准确时,就需要显示的传入类型函数
默认情况下,泛型函数的类型变量T可以代表多个类型,这导致无法访问任何属性
添加泛型约束收缩类型,有两种方式
1、指定更加具体的类型
2、添加约束
// 创建接口
interface Ilength {length:number}
// 使用泛型创建一个函数 只要满足具有length属性就可以
function id<T extends Ilength>(value:T):T{
console.log(value.length)
return value
}
// 调用泛型函数
// 以number类型调用泛型函数
let string = id([1,8])
let stringLength = id({length:999})
泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如:第二个类型变量受第一个类型变量的约束)
// 类型约束(第二个参数受第一个参数的约束) keyof关键字接收一个对象类型,生成其键名称(可能是字符串或者数字)的联合类型
function keyProps<T, key extends keyof T>(obj:T,key: key) {
return obj[key]
}
keyProps({name:'你好',age: 111}, 'name')
如果传的是一个字符串类型,也可以访问字符串类型的所有方法以及键名
keyProps('string','slice')
keyProps('string',1) // 此处1表示索引
keyProps([1,3,4],1) // 此处1表示索引
解释: 1、在接口后面添加<类型变量>,那么,这个接口就变成了泛型接口
2、接口类型变量,对接口中所有的其他成员可见,也就是接口中所有成员都可以使用类型变量
3、使用泛型接口时,需要显示指定具体的类型
interface IdFunc <T> {
ids:(value:T)=>T
id: ()=>T[]
}
let obj:IdFunc<number> = {
ids: (value) => value ,
id: ()=>[111,2]
}
实际上,JS中的数组在TS中就是一个泛型接口
mac:可以使用* command*快捷键查看数组的类型文件
class也可以配合泛型来使用
比如 React的class组件的基类Component就是泛型类,不同组件有不同的props和state 解释
React.Component泛型类两个类型变量,分别指定props和state类型
创建泛型类
类似于泛型接口,在创建class实例时,在类名后面通过<类型>来指定明确的类型
当类中使用了constructor 可以自动推断参数类型 因为我们传了参数name // 最好明确指定类型
// 1、这种情况需要明确指定类型
class GenerNumber<numT> {
name: numT
add: (x:numT, y:numT)=>numT
}
const number = new GenerNumber<number>()
// 2、这个情况可以省略类型
class GenerNumberTwo<numT>{
name: numT
add: (x:numT, y:numT)=>numT
constructor(name: numT) {
this.name = name
}
}
const nameOne = new GenerNumberTwo('nihao')
它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用
1.Partial<Type>
2.Readonly<Type>
3.Pick<Type,Keys>
4.Record<keys,Type>
interface Props {
id: number,
name: string
}
type PropsTwo = Partial<Props>
interface Props {
id: number,
name: string
}
type PropsTwo = Pick<Props,'id'>
type ObjType = Record<'a'|'b',string[]>
// 相当于
type ObjTypeTwo = {
a: string[],
b: string[]
}
let obj: ObjTypeTwo={
a: ['kkk'],
b:['kkk']
}
使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)此时,就用到索引签名类型了
// 对象中的键是sring类型的
interface ObjType {
[key:string]: string
}
const obj: ObjType ={
one: '1'
}
数组的键的索引是数值类型
映射类型:基于旧类型创建新类型(对象类型)减少重复,提高开发效率
映射类型只能在类型别名中使用,不能在接口中使用
type propKeys = 'x'|'y'
type Type2 = {[key in propKeys] : number}
// 相当于:
type Type1 = {x : number, y : number}
映射类型除了可以根据联合类型创建新类型之外,也可以根据对象类型来创建
type Type1 = {x : number, y : number}
// keyof表示获取所有键的联合类型
type type3 = {[key in keyof Type1]:string}
partial将type的所有属性设置为可选
刚刚用到的T[P]语法,在TS中叫做索引查询(访问)类型
作用:用来查询属性的类型
索引查询类型的其他使用方式:同时查询多个索引类型
类型声明文件:用来为已存在的JS库提供类型信息
TS中有两种文件类型:
ts文件
1.既包含类型信息又可以执行代码
2.可以被编译为.js文件,然后执行代码
3.用途:编写程序代码的地方
.d.ts文件
pakage.json中会指定引入
ts会首先查看lodash文件中有没有类型声明文件,没有的话在@types文件中去查找
ts官方提供链接查看是否有声明文件
https://www.typescriptlang.org/dt/search?search=lodash
创建自己的类型声明文件:1、项目内共享类型 2、为已有JS文件提供类型声明
1、项目内共享类型,如果多个.ts文件中都用到同一个类型,此时可以创建.d.ts文件提供该类型,实现类型共享 操作步骤:
1、创建index.d.ts类型声明文件
2、创建需要共享的类型,并使用export导出(TS中的类型也可以使用import/export实现模块化功能)
3、在需要使用共享类型的.ts文件中,通过import袋鼠即可(.d.td后缀导入时,直接省略)
根据TS 编辑的配置项在tsconfig.ts中配置
react-app-env.d.ts React项目默认的声明文件
react-app-env.d.ts:React项目默认的类型声明文件
三斜线指令:指定依赖的其他类型声明文件,types表示依赖的类型声明文件包的名称
///
解释:
告诉TS帮我加载react-scripts这个包提供的类型声明
react-scripts的类型声明文件包含两个部分
1、react、react-dom、node等类型
2、图片、样式等模块的类型,以允许在代码中导入图片、SVG文件
TS会自动加载该.d.ts文件,以提供类型声明,(通过修改tsconfig.json中的include配置来验证)
tsconfig.json指定:项目文件和项目编译所需的配置项
文档
现已 CRA项目中的配置项为例
1、tsconfig,json文件所在目录为项目根目录(与pakage.json同级)
2、tsconfig.json可以自动生成,命令:tsc–init
tsconfig.json的解释说明
// 所有的配置项都可以通过鼠标移入的方式,来查看配置项的解释说明
- [tsconfig文档链接](https://www.typescriptlang.org/tsconfig)
```json
{
// 编译选项
"compilerOptions": {
// 生成代码的语言版本
"target": "es5",
// 指定要包含在编译中的library
"lib": [
"dom", // 关于dom相关的类型所有的类型都提供了
"dom.iterable",
"esnext"
],
// 允许ts编译器编译js文件
"allowJs": true,
// 跳过声明文件类型检查
"skipLibCheck": true,
// es模块互操作,屏蔽ESModule和CommonJS之间的差异
"esModuleInterop": true,
// 允许通过import x from y 即使模块没有显示制定default导出
"allowSyntheticDefaultImports": true,
// 开启严格模式
"strict": true,
// 对文件名称强制区分大小写
"forceConsistentCasingInFileNames": true,
// 对swicth语句启用错误报告
"noFallthroughCasesInSwitch": true,
// 生成代码的模块化标准
"module": "esnext", // 是一个始终表示 JavaScript 下一个版本的名称。
// 模块解析(查找)策略
"moduleResolution": "node", // 按照node中的模块查找策略处理ts
// 允许导入扩展名为.json的模块
"resolveJsonModule": true,
// 是否将没有import/export的文件视为旧(全局而非模块化)脚本文件
"isolatedModules": true,
// 编译时不生成任何文件只进行类型检查
"noEmit": true,
// 指定讲jsx编译成什么形式
"jsx": "react-jsx"
},
// 指定允许ts处理的目录
"include": [
"src"
]
}
静态类型检查
React+TS备忘单
class Hello extends React.Component<Props>{
// 提供默认属性
static defaultProps:Partial<Props> = {
name: '哈哈啊',
age: 13
}
render(): React.ReactNode {
const {name,age} = this.props
return (<div>{name}{age}</div>)
}
}
function App() {
return (
<div className="App">
<Hello name='你好'></Hello>
</div>
);
}
仓库地址:https://github.com/FlowerGHQ/react-ty
https://blog.csdn.net/Y_Z233/article/details/125450058
https://cn.vuejs.org/guide/typescript/composition-api.html#typing-ref