- 类型声明
- 类型声明是TS非常重要的一个特点
- 通过类型声明可以指定TS中变量(参数、形参)的类型
- 指定类型后,当为变量赋值时,TS编辑器会自动检查是否符合类型声明,符合则赋值,否则报错
- 指定类型后,当为变量设置了类型,使得变量只能存储某种类型的值1
- 语法
let 变量:类型
let 变量:类型 = 值
function fn(参数:类型,参数:类型):类型{
...
}
像JS中let a;
,a = 10;
,a = 'hello'
都是可以的。但是由于就是这么写,实际上给我带来了很多的安全隐患,可能改成了其他的类型;容易出错,而且错误非常的难找
ts的很大的作用就是把js变成了一个静态类型的语言,它可以给我们变量指定类型
//声明一个变量a,同时指定它的类型为number
let a:number
//a的类型设置为了number,在以后的使用过程中a的值只能式数字
a = 10
//a='hello'//此行代码会报错,因为变量a的类型是number,不是赋值字符串
提示:
let a: number
不能将类型“string”分配给类型“number”。
执行了tsc
虽然提示报错了,但是还是会生成.js
文件。这时ts的一个特点: 即使有错误但还是会编译成功
- 1.这种是符合
js
规范的 - 2.也是为了刚刚接触
ts
的人更能去接受并适应这个ts
编译
tsc hello.ts
ts指定类型了以后用tsc
生成js
代码里面
ts中:
let a:number
在js中:
var a;
这是因为ts有一些特点:他需要从ts文件编译为js文件是可以编译成任意版本的js。
好处是它可以确保代码的兼容性
。在不同的浏览器都可以去执行
我们说ts就是把js变成一个静态语言,但是实际上这种写法(let a:number
)我们在ts中写的并不多
一般情况就是我们声明完变量直接赋值:
//声明完变量直接进行赋值
let c: boolean = true
这种写法还是不常用,在ts中
//如果变量的声明和赋值是同时间进行的,TS可以自动对变量进行类型检测
let c = false
c=true
c=1234
这样c=1234
还是会报错
如果对变量进行声明,则要注意: 如果你是先声明在赋值:**let a:number;a=10**
;如果你是声明和赋值同时进行,像**let c = false;**
这种你就不用再去写,它会给你自动判断类型
我们说js这个类型给我造成的困扰,往往不是变量,其实是方法(function
):
//JS中的函数是不考虑参数的类型和个数
function sum(a,b){
return a+b
}
这个时候你注意了,调用时
sum(123,456)//579
准备一个index.html
把生成的js
放进去:
<html>
<head>
<meta charset="utf-8" />
<title>ts基本类型测试</title>
</head>
<body>
</body>
<script src="./hello.js"></script>
</html>
结果:
579
如果我们这时候传的是:
sum(123,"456")
则呈现的是
"123456"
虽然js的代码再开发过程中给我产生了便利但是也给我们造成了安全隐患
ts可以帮我们限制类型
function sum(a:number,b:number){
return a+b
}
console.log(sum(123,456))
虽然编译还是会通过,但是会给你提示和报错
而且值多了少了都会报错
而其中return a+b
有返回值类型,既然是值,也可以指定参数
function sum(a:number,b:number): number{//外面的number表示指定返回值的数据类型
return a+b
}
let result = sum(123,456) //这样result的值也可以自动被判断出来
而且,如果写成return a+'hello'
ts也会提示报错
TS中的类型
类型 | 例子 | 描述 |
---|---|---|
number | 1,-33,2.5 | 任意数字 |
string | ‘hi’,”hi”,hi |
任意字符 |
boolean | true、false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值(或undefined) |
never | 没有值 | 不能是任何值 |
object | {name:’我’} | 任意的js对象 |
array | [1,2,3] | 任意js数组 |
tuple | [4,5] | 元素,TS新增类型,固定长度数组 |
enum | enum{A,B} | 枚举,TS中新增类型 |
ts:js的超集(在js上扩展)
字面量
//也可以直接使用字面量进行类型声明
let d:10
d=10
d=11//只能赋值为10,如果赋值为11就会提示报错
学过后端的人,你可以把字面量
看作是常量
一般字面量我们是这么写的:
//'|'表示一个'或'的意思
let e: "male" | "female"
可以使用**|**
来连接多个类型(联合类型)
let f: boolean | string
//这样可以赋值俩个类型的值
f=true
f='hello'
字面量的作用就是**限制取值范围在哪几个值之间**
any
//any 表示的是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测
//使用TS时,不建议使用any
//显示any
let d:any
//声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any.(隐式any)
//let d
d=10
d='hello'
d=false
这跟我们原生的js没什么区别,所以使用TS时,不建议使用any,如果直接写let d
,d的值也是any
any 表示的是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测
因为ts是在js上进行拓展,js身上有很多值是不确定类型的时候,你可以用any,但是其实我们还有一个选择:
unknown
//unknown表示未知类型的值
let e: unknown
那么any
和unknown
有什么区别?
这样
s
的类型也就出问题了
只要被any
沾着变,谁的类型就出问题
但是unknown
就不一样了
let s: string
//当把any类型的值赋值给字符串的时候,可以赋值
//d的类型是any,它可以赋值给任意变量
s = d
let e: unknown
let s:string
e='hello'
s=e//会提示报错
它看你的不是e
的'hello'
而是e
这个变量的类型是unknown
(也就是这个e的类型是未知的)
提示:
let s: string
不能将类型“unknown”分配给类型“string”。
any
和unknown
的区别
- any就是你给别人赋值的时候,别人也不查ts自动判断类型了
- unknown只是霍霍自己
//unknown 实际上就是一个类型安全的any
//unknown 类型的变量,不能直接赋值给其他变量
解决提示报错问题:
if(typeof e === "string"){//可以做个类型判断
s = e
}
麻烦归麻烦,但是避免了后面类型出问题
/**
* 语法:
* 变量 as 类型
* <类型>变量
* /
//类型断言,可以用来告诉解析器变量的实际值
s = e as string
//另一种写法
s = <string>e
有些时候,有一些变量我们的ts编译器它不知道,但是我们知道。s=e
它给我们报错,我们要告诉ts:e就是字符串类型,这就会用到 类型断言
void 和 never
在变量中用的不多,主要用来对我们设置一个函数的返回值:
function fn(){}
这样写默认返回的就是void
//void 用来表示空,以函数为例,就表示没有返回值的函数
function fn(): void{
return;//可以这么写
//return null;//可以这么写
}
never
//nerver 表示永远不会返回结果
function fn2():never{}
void
虽然没有返回值,但严格来讲也是有返回值never
:什么都没有,连空都没有
never它是用来**报错**
的
function fn2(): never{
throw new Error('报错了')
}
object
//object表示一个js对象
let a: object
实际上它并不是那么的实用
对象我可以直接这么写:
a = {}
a = function(){}
在js中一切都是对象
其实我们在限制一个对象的时候我们更想限制的时对象包含有哪些属性,而不是限制他是不是一个对象
我们限制对象其实我们可以怎么写?
//{}用来指定对象中可以包含哪些属性
//语法:{属性名:属性值,....}
let b:{name:string}//这就表示说,我b这个变量指向的是一个对象,同时这个对象里面得有一个name属性
//b={}
/*
let b: {
name: string;
}
类型 "{}" 中缺少属性 "name",但类型 "{ name: string; }" 中需要该属性。ts(2741)
hello.ts(46, 8): 在此处声明了 "name"。
*/
b={name:'我'}
这样子定义你的要求(b={name:'我'}
)和赋值的要求(let b:{name:string}
)必须一致,差一点都不行
如果说:
let b: {name:string,age:number}
b={name:'我'}
这样b还会报错,怎么办呢?
//在属性后面加一个?表示属性时可选的
let b: {name:string,age?:number}//age后加一个?
b={name:'我'}
这个?
就证明可选的,有也行,没有也行
又出现一个问题:
let c:{name:string}
c={name:'你',a:1,b:2}
除了name意外谁也不认,其他的属性有没有都行,不去一个一个添加,可以用一种方式去表示可以用任意属性:
//propName就是属性名,你可以写任意的
//[propName:string]:表示任意属性名
//':any':表示任意类型,如果写成string就表示a、b添加的所有值都得是字符串
let c:{name:string,[propName:string]:any}
c={name:'你',a:1,b:2}
这样都不会再报错了
如果我写的是一个function
let d:Function
我希望限制d函数的结构
我们这里可以使用一个类似于箭头函数的一个结构语句:
/*
设置函数结构的类型声明
语法:(形参:类型,...)=>返回值类型
*/
//这就代表了我这个d它是一个函数,'(a:number,b:number)=>number'也是一个类型声明
//我希望这个d它的函数有俩个参数,而且俩个参数的类型都是number而且返回值也是number
let d:(a:number,b:number)=>number
//那我这个时候再给d进行赋值的时候(a,b名无所谓)
d = function(n1,n2){
return n1+n2
}
/*这样写也可以
d = function(n1,n2):number{
return n1+n2
}
*/
其实我们是利用类似于箭头函数的形式来设置函数结构的类型声明
array(数组)
在js中也是非常常用的一个类型
但是在js里的数组是没有类型的概念的
如果什么类型都传入的话,这个数组实际上处理起来很麻烦,或者存储性能并不是特别的高,所以我们在开发中 一个数组都是存储相同类型的值
所以我们声明数组是声明是什么类型的数组
比如:
//string[]表示:字符串数组
let e: string[]
这个时候我们在赋值的时候都只能是字符串,不是字符串的数组会提示报错
数组有几种表示的方式:
/*
数组的类型声明:
类型[]
Array<类型>
*/
//number[]:表示数值数组
let f:number[]
//跟上面的是一样的,都表示数值类型的数组
let g:Array<number>
以上都是对js类型进行的一些限制,ts还对js有了一些扩展
tuple(元组)
元组就是固定长度的数组
元组要比数组存储效率更好一点,因为元组长度是固定的;它的变化会少一些
什么时候用元组:就是当你的这个数组里面的值数量是固定的
元组怎么写:
/*
元组:元组就是固定长度的数组
语法:[类型,类型]
*/
let h:[string,string]//这就表示我创建了一个元组
不能多加少加元素,也不能元素类型不对
enum(枚举)
let i:{name:string,gender:string}
i = {
name: '我'
gender:'男'
}
但是我们一般开发的时候都不会这么去存,因为存这个值的目的就是能判断出我这个对象到底是男
还是女
所以我判断它到底是男还是女我可以:
console.log(i.gender === '男')
如果我不存男而是存male
其实字符串存到数据库里面相对来说存的会比较大一点,所以我们希望我们存的数据相对来说要小的话,gender的值基本上是固定的,像这种值基本上是固定内的,一般我们用枚举
枚举就是把我们所有的可能的情况全都给我们列出来
那我们枚举怎么写:
//定义枚举就现以Enum开头,表示是我们在这定义了一个枚举
enum Gender{
//里面写你到底有哪些值
Male,
Female
}
这就定义好了一个枚举类
可以这么写了:
let i: {name:string,gender:Gender}
i={
name:'我',
gender: Gender.Male
}
我就表示'我'
是一个男生
这俩个值,在我们ts
转的时候Male
就转成0了,Female
就给你转成1了
好处是:Gender.Male
我一看就知道是男
判断也是一样:
console.log(i.gender = Gender.Female)
你可以手动写
enum Gender{
//里面写你到底有哪些值
Male = 1,
Female = 0
}
但是没必要
说到俩个类型用|
去连接
//表示我们j的类型是string或number
let j: string | number
其实我们这也可以用一个&
连接
//&:表示‘且’(同时)的意思
let j:string & number
但是你不能同时满足**string**
和**number**
所以这是一个错误的写法
怎么用:
//&可以用来连接俩个对象
let j: {name:string} & {age:number}
j = {name:"我",age:18}
表示俩种类型的j既要满足name中的对象,又要满足age中的对象
类型的别名
当出现俩个属性声明相同的情况下:
let k:1|2|3|4|5
let l:1|2|3|4|5
l
的范围跟k
是一样的,这个时候就很麻烦了:
- 首先这个类型很长
- 我这个类型要重复的去使用
type myType = string//这样我们就等于创建了一个类型别名
let m:myType //这就等价于string
所以可以:
type myType = 1|2|3|4|5
let k:myType
let l:myType
k=5
//k=6//报错
这些就是TS中基本类型