为什么需要Typescript
在编写Javascript代码时,会出现 类型不明确 或 函数参数不明确的问题。 比如我们可以对一个变量赋各种类型的值,也可以给一个函数传入各种类型的参数没,如下所示:
let a = 100
a = 'hello'
function add(x,y){
return x+y
}
console.log(add(100,200)) //300
console.log(add(100,'200')) // '100200'
这会带来一些混乱,比如我们实现了一个函数add用来实现加法,我们期待用户能传入两个number类型的值,但是当用户传入number和string类型时,返回的结构就不会符合预期。所以需要对变量,参数等类型进行限制。
TypeScript 是一种基于 JavaScript 构建的强类型编程语言, 可以理解为js的扩展,增加了对于js中宽松的类型的限制。但是需要注意,TypeScript无法直接运行在浏览器或者nodejs环境中,需要使用ts专用的编译器(或者webpack ts-loader/Babel)将ts代码转换成js运行。
npm install typescript 安装ts编译工具
编写ts与编写js类似,我们创建xxx.ts文件来代替xxx.js 并在其中书写ts代码。
将上述代码改写成ts代码,我们可以发现,在试图修改number类型变量时和在向add函数中传入错误类型参数时会产生ts报错
指定变量类型
js中的值有类型,但是变量没有类型,可以指向任何类型的值, 但是在ts中,变量也是有类型的,并且只能赋予满足其类型的值,其具体写法为:
var/const/let 变量名: 变量类型 = 值
如 let a: number = 100 表示a变量只能赋予数字类型的值,否则会报错。
当然,如果在声明变量时直接赋值,会默认变量为赋予值的类型,如 let a = 100 等价于上面的写法。
如果即不显式声明变量的类型,也不赋初始值,如 let a ; 则默认变量的类型为any,此时ts的检查无效 ,a可以赋予任何值。
限制函数类型
函数会限制两个部分,一个是参数类型,一个是返回值类型 ,一般可以采用如下写法
function 函数名称(参数: 类型,参数: 类型 ...): 返回值类型 {函数代码}
或
const 函数名: (参数: 类型,参数: 类型 ...) => 返回值类型 = function(参数..){函数代码}
例如,上面的add函数,可以采用如下的写法, 表示传入两个数字类型的参数,返回一个数字类型的值,这样在传入的值和参数类型不一致时,会产生ts报错来提示调用者。
function add(x: number,y: number): number{
return x+y
}
const add: (x: number,y: number) => number = function(x,y){
return x+y
}
const add: Function = function(x,y){
return x+y
}
使用Function也可以来判断一个变量是否为函数类型,但是这样使用无法限制函数的参数和返回值,意义不大,一般不会这样使用。
限制对象的类型
最简单的限制对象方式,可以直接使用object,如const obj: object = {} 但是同Function一样,这样只能限制赋给obj的不是个简单类型,赋一个函数 或者任意属性的对象都不会报错,这样的意义不大。
通常情况下,我们使用如下方式来限制对象的结构
const obj: {
name: string,
age: number,
gender: string,
score: number
} = {
name: 'bill',
age: 18,
gender: 'male',
score: 100
}
obj 的类型中,规定了其每个属性的名称和对应value的类型,赋给obj的值也必须同该类型具有相同的结构,否则会报错。
如何定义一个任意属性对象的类型?
你可能会想到使用 object,但是object的检查太宽泛了,你甚至可以给object类型的变量传递一个函数, 通常,我们使用以下的方法
1. 使用类型索引
const obj: {
[P: string]: any
} = {
name: 'bill',
a:1,
B:2,
c:3,
d: 'hello world!'
}
[P :string] 为类型索引,表示属性值可以是任何字符串类型,value为any,表示值可以是任何类型
2. 使用Record<k,v>
const obj: Record<string,any> = {
name: 'bill',
a:1,
B:2,
c:3,
d: 'hello world!'
}
Record<属性类型,值类型> 如上方式也可以定义一个任意类型的对象
如果想要求obj中必须包含一些属性,可以使用
const obj: {
name: string,
[P: string]: any
} = {
name: 'bill',
a:1,
B:2,
c:3,
d: 'hello world!'
}
或者使用交叉类型
const obj: {name: string}&Record<string,any> = {
name: 'bill',
a:1,
B:2,
c:3,
d: 'hello world!'
}
字面量限制
如 const a: 10 = 10 那么a 只能为数字10 若赋值a=11会报错
也可以结合联合类型 const a: 1|2|3|4|5 = 1 (可以是 1, 2, 3,4,5中任意一个)
unknown any never
any代表任意类型,设置为any类型的变量相当于关闭ts类型检测,可以赋任何值,并且可以将其赋给任何变量。 尽量避免使用any的情况。但是并不是说不能用any,对于实在无法写出类型的,可以使用any,但是要避免"any一把梭"的情况!
unknown代表不知道的类型,和any类似,区别在于,any类型的变量可以赋任何类型的值,并且可以赋给任何类型的变量。unknow类型的变量虽然可以赋任何类型的值,但是在赋给其他非unknown类型的变量时,会报错,如:
unknow可以有效避免未知类型传递下去,面对未知的类型,unknown相比any是更好的选择
never表示逻辑上不应该发生的情况,我们在工作中不会常遇到使用 never 的情况
never可以赋给任何类型的变量,但是其自身不允许赋予任何非never类型的值或者变量
never出现的情况可能是:
// 如 不存在一个数满足 又是1 又是 2 又是 3
const a:1&2&3 = 1 // a never
// 如Exclude把所有的值都排除
type TType = Exclude<"name"|"age",'name'|'age'> // TType never
或者 一个函数不会返回值,如抛出异常,那么这个函数的返回值应该是never,需要注意void和never的区别,不写return 返回null undefined 都属于返回值为void 但是抛出异常中断函数属于never(逻辑上的错误)
function foo(): never{
throw new Err()
}
交叉类型"&" 和 联合类型 "|"
交叉类型& 表示必须满足所有子类型 如
let a: 1&2&3&4
// a为never 因为没有任何一个值满足 1&2&3&4
let obj: {name: string}&{age: number}&{gender: string}
// 这里等价于
let obj: {
name: string,
age: number,
gender: string.
}
// 也就是说,三个类型都必须要满足,要包含name age gender
obj = {
// 报错,少任何属性 或者多出来任何属性都不行
name: 'bill',
age: 18,
}
联合类型| 表示满足任意一个或多个子类型即可 如:
const a: 1|2|3|4|5|'hello' = 'hello'
// 这里a为 1,2,3,4,6,'hello'都可以
let obj:{age: number}|{score: number}|{name: string} = {
// 满足 {age: number}|{score: number}|{name: string}
name: 'bill',score: 100 ,age:18
}
// 这里obj中的属性,为三个子类型任意一个或者多个都可以
obj = {
age: 18, // 满足{age: number}
}
obj = {
// 满足 {age: number}|{name: string}
name: 'jack',
age: 18,
}
obj = {
name: 'jack',
age: 18,
a: 1 // 多出一个属性,相当于 a: {name: string,age; number,a: number} 不满足任何子类型的组合
}
obj = {
name: 'jack',
age: '18' // 类型错误,相当于 a: {name: string,age; string} 不满足任何子类型的组合
}
注意⚠️ never和任意类型的联合类型,都会去掉never 如
let a: never|any // a为any
never和任何类型的交叉类型 都是 never
let a: never|any // a为never
数组限制
Javascript中的数组不会限制数组元素类型的一致性, 但是我们使用的时候最好统一数组项的类型,ts中可以使用 类型[] 的方式限制 或者使用 Array<Type> 表示某个类型的数组
const list: number[] = [1,2,3,4,5,6,7,8,9,10]
const list :Array<number> =[1,2,3,4]
ts还可以限制数组的长度,通常称为元组tuple ,其使用方法为 [项1类型,项2类型,... 项N类型]
类型需要一一对应
const tuple: [string,string]=['hello','world']
枚举enum
ts中支持枚举的定义,其具体定义形式为:
enum EnumName{
枚举值定义
key = value
}
如,定义性别枚举
enum EGender{
"male" = 0,
"famale" = 1
}
使用
const info: {
name: string,
age: number,
gender: EGender
} = {
name: 'bill',
age: 18,
gender: EGernder.male
}
注意,enum在ts编译成js之后是会生成一个对象的,可以在js中使用,但是其他的ts类型在编译成js之后会消失,无法在js中使用一个类型
类型别名 type
ts中支持把一个类型保存下来,使用type关键字,可以将类型,或者多个类型的联合或者交叉的结果暂存,方便灵活使用 如下:
type MyType = 1 | 2 | 3 | 4 |5 ;
const mytype: MyType = 1