1. 变量的类型注释
在JavaScript中, 变量的复制相对灵活, 除了const
外, let
和var
声明的变量可以赋予不同类型的数据
例如:
let val = 'string'
console.log(typeof val)
// 变量val的类型为string 类型
val = 10
console.log(typeof val)
// 变量val的类型为number 类型
val = true
console.log(typeof val)
// 变量val的类型为boolean 类型
示例中, 变量val
可以赋值不同类型的变量, 而变量的类型取决于赋予变量的值.
如果我们需要限定变量的类型, 只允许给变量赋予某一种类型或几种基本类型 的联合类型, 我们就需要使用TypeScript给变量添加类型注释
1.1 赋初值
基础变量声明的语法
let [变量]:[类型注释] = 值
类型注释需要用来描述变量可以被赋予那些类型的值.
// 1. 这样会报错,声明的变量是number类型,却被赋值为string类型的值
let a:number = 'hello'
// 2. 这样也会报错,声明string类型后中途重新赋值了其他类型的值
let a:number = 20
a = 'hello'
这种方式实现的变量命名的好处,那就是在赋值的时候确定变量的类型,如果存值和声明的变量类型不符合就会报错
声明类型的变量,将只能赋予相同类型的值
如果声明变量赋初始值,在大多数情况下,添加类型注释并不是必须的, 原因在与TypeScript会根据我们赋予的初始值来自动推断变量的类型.
例如:
let a = 'hello'
// let a: string
a = 10
// 不能将类型“number”分配给类型“string”
TypeScript更加初始赋值'hello'
推断变量a
为string
类型, 此时在将其他类型数据赋值给变量a
就会报错
1.2 不赋初始值
语法
let [变量]:[类型注释]
- 如果只创造了变量并规定类型,那么这个变量默认值就是undefined
- 如果后面需要赋值时,还是只能按照 声明的变量类型来赋值,否则就会出错
let num:number;
console.log(num) // undefined
num = 10 // ok
num = 'hello'
// 错误:不能将类型“string”分配给类型“number”
声明一个变量不管什么类型, 初始不赋值,变量默认为undefined
,这是遵循JavaScript的语言规范,
那么为什么undefined
类型的值可以赋值给number
类型的值呢? 那么先来了解一下TypeScript类型
2. 数据类型
TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。
TypeScript 数据类型有:
- 数字类型 number
- 字符串类型 string
- 布尔类型 boolean
- undefined undefined
- null null
- 数组类型 []
- 元组 []
(数组的特殊形式)
- 对象类型 {}
- 函数类型 Function
- 任意类型 any
- void void
- never never
- unknown 不认识,代表 任何值,类似于any
- 枚举 enum
2.1 字符串,数字,布尔类型
和JavaScript 字符串,数字,布尔类型一样,并且所有数字都是浮点数。以及可以使用ES6字符串模板
// 字符串类型
let username = '张三'
let person :string = `字符串模板${username}`
console.log('person', person)
// 数字类型
let num: number = 30
console.log('num', num)
// 布尔类型
let bol: boolean = true;
console.log('bol',bol)
2.2 数组类型
JavaScript定义数组可以通过字面量和构造函数, TypeScript在添加数组的类型注释也不同的方式
第一种就是使用字面量的方式
// 正确的写法
let arr: number[] = [10, 20, 30, 40];
let arr: string[] = ['a', 'b', 'c', 'd'];
// ...
// 错误的写法
let arr:[number] = [10, 20, 30, 40];
// 这种写法指表示确定数组第一项的数据类型,其他的没有确定,报错
number[]
这种数组类型的定义方式, 其中[]
表示是数组类型,number
是基本数据类型, 表示数组每一项都是number
类型
第二种:使用内置的泛型Array
// 或者使用泛型
let arr: Array<number> = [10, 20, 30, 40];
let arr: Array<string> = ['a', 'b', 'c', 'd'];
// ...
上面的示例中数组内所有的值都是同一种类型, 但在很多情况下数组内会有不同数据类型的值,
那么就可以使用any
类型或联合类型
使用any
类型定义数组内的元素类型
let arr: any[]= [10, 'hello', true, null];
let arr: Array<any> = [10, 'hello', true, null];
// any表示任意类型,表示数组内每一项都可以是任意类型
使用联合类型数组, 联合类型使用|
符合,
联合类型的前提是你明确的知道,数组内只能存那些类型的数据
例如:我们希望一个数组只能存number
,string
两种类型的值
let arr:(string | number)[] = ['hello' , 20, 'world']
let arr:Array<string | number> = ['hello' , 20, 'world']
(string | number)[]
类型注释中[]
表示为数组类型,(string | number)
就是联合类型,表示数组没一项可以是string
类型或number
类型.满足一个即可
2.3 元组类型
元组类型其实就是另外一种Array
数组 类型. 它确切的知道数组中包含多少元素, 以及它在特定位置包含哪些类型
元组类型表示一个已知元素数量和每一项类型的数组,各元素的类型不必相同
其实元祖类型就是单独定义数组的每一个数据的类型.
// 正确的写法
let arr:[number,string, boolean] = [10,'string',true]
// 错误的写法
let arr:[string,string, boolean] = [10,'string',true]
// 编译时报错,Type 'number' is not assignable to type 'string'.
// 不能将数字类型的值赋值给字符串类型
// 注意定义数据类型和数据的数量保持一致, 数量不同则会报错
let arr:[string,string, boolean] = [10,'string']
// Property '2' is missing in type '[string, number]' but required in type '[string, number, boolean]'.
也可以先声明元组类型的变量,之后再重新赋值
let a:[string,number,number,boolean,object]
// 这种 写法没问题
a = ["hello",1,2,true,{age: 18}];
// 这种写法不行,上面创建变量时,要求的类型和值的类型按照顺序对不上
a = [1, "hello" , 2, true,{age: 18}];
// 这个写法也不可以,因为少了一个对象
a = ["hello",1,2, true];
TypeScriptde的一个好处就是,会在代码执行前的类型检查阶段抛出错误, 不想JavaScript只能在代码运行时,才知道发生了什么错误
2.4 object 引用数据类型
object
是一个特殊的类型, 指定是任何不是原始值(string, number,bigint,boolean,symbol,null, undefined)的值.
这与空对象类型{}
不同, 也与全局类型Object
不同
object
表示的是JavaScript中的引用类型,只要是具有属性的类型都可以使用object
类型
// 函数
const fn:object = () => console.log('fn')
// 数组
const arr:object = [10,20]
// 对象
const obj:object = {name:'hello', age:18}
因此这个类型在使用很少, 大多通过更详细的类型添加类型注释.
例如:
// 函数类型注释
const fn:() => void = () => console.log('fn')
// 数组类型注释
const arr:number[] = [10,20]
// 对象
const obj:{name:string,age:number} = {name:'hello', age:18}
2.5 对象类型
除了基本数据类型, 最常见的类型是对象类型. 对象类型是值任何带有属性的JavaScript值
对象类型几乎是所有的属性都要定义对象类型.
通过关键字object
定义对象类型
// 对象类型
let ob:object = { a: 10 }
这种类型注释的对象并不限定属性数量与属性值的类型. 可以赋值任意类型
也可以通过字面量方式,列出所有属性以及属性类型来进行类型注释
let student:{name:string,age:number} = {name:'张三',age:18}
2.6 Function 类型
Function
为全局类型, 描述JavaScript中所有函数值的属性, Function
类型总是可以调用类型值的特殊属性, 这些调用返回any
, 一般也不常用
function example(fn:Function){
return fn(1,2)
}
参数fn
是一个具有函数属性的函数类型, fn()
的调用是一个无类型的函数调用, 因为返回类型为any
类型不太安全.最好避免使用,.
可以使用更加详细的函数类型注释
例如:
function example(fn:() => void){
return fn(1,2)
}
2.7 任意类型 any
TypeScript也有一个特殊的类型: any
类型,当你不希望某个特定的值导致类型检查错误时,可以使用它
通常当你声明一个变量, 没有确定初始类型,也没有赋初始值是,或者赋初值为undefined或null时, 变量的类型默认为any
类型.
也就是说:当您不指定类型,并且 TypeScript 无法从上下文中推断出它时,编译器通常会默认为any
.
any
类型的变量赋值任何类型的数据都不会报错
let a = null
// 或者
let a = undefined
// 或者
let a;
// let a: any
a = 'aa'
console.log(a) // 'aa'
a = 30
console.log(a) // 30
一般来说并不建议大量使用any
类型.当你明确知道一个变量之后不会被赋予其他类型的数据, 那么就可以在声明变量时添加详细的类型注释;
只有当你在不确定变量未来会赋予什么样类型的值时, 可以尝试使用
注意:
any类型的变量会跳过类型检查
例如:
let obj: any = { x: 0 };
// 下面代码将没有一行代码编译错误
obj.foo();
obj();
obj.bar = 100;
obj = "hello";
const n: number = obj;
实例中变量obj
为any
类型, 因此obj
身上的所有操作都会跳过类型检查, 因此在TypeScript编译代码时不会报错.
但是执行编译后的代码可能会出错.
2.8 空类型 void undefined null
Void 表示没有任何类型 空
通常声明一个Void 类型没什么意义,因为这个类型的变量只能赋值为undefined和null
let a: void = undefined;
console.log(a)
let a: void = null;
console.log(a)
// 赋值其他类型的值就会报错
let a: void = 123;
console.log(a)
// 报错:不能将类型“number”分配给类型“void”
在TypeScript里,undefined和null 两者各自都有自己类型分别叫做undefined和null;和void相似,他们的类型本身用处不大:
// 声明一个undefined类型变量
let und: undefined;
und = undefined;
und = null;
console.log(und)
// 声明一个null类型的变量
let nul: null
nul = null;
und = undefined;
console.log(nul)
和void
一样, undefined
和null
类型只能赋值undefined
或null
值, 本身意义不大
默认情况下,null和undefined 是 所有类型的子类型
就是说你可以把null和undefined赋值给其他类型的变量
let num:number = undefined;
let num:number = null;
let str:string = undefined;
// ....
但是,不能将其他类型的值赋值给void,undefined,null类型的变量
let un:undefined = 12;
let aa:null = 'aa';
let bb:void = true;
这样写会报错
2.9 never类型
never类型表示的是那些永不存在的值的类型
这个严格来说算不上新的数据类型,只是开发者对于一些值所起的作用的判断而已
比如:
- 总是会抛出异常,throw错误或是返回一个error类型的数据
- 根本就不会有返回值的函数表达式(死循环函数)
// 没有返回值
function error(msg:string):never {
throw new Error(msg)
}
// 一旦有了返回值never类型就报错
function error():never {
retrun new Error('something failed')
}
// 报错: 不能将类型“Error”分配给类型“never”。
// 哪怕没有显示的return 也会报错, 因为函数有默认返回undefined
function error():never {
new Error('something failed')
}
// 返回“never”的函数不能具有可访问的终结点。
// 死循环函数
function infiniteLoop():never{
while(true){
console.log('帅")
}
}
never类型是任何类型的子类型,任何其他类型的值都不能赋值给never
类型.即使any也不可以
let n:never;
n = 12
// 不能将类型“number”分配给类型“never”
2.10 unknown 类型
unknown
类型代表任何值, 这类似于any
类型, 但更安全, 因为用值做任何事情都是不合法的
例如:
function fn1(a:any){
// 调用any类型的属性是合法的
// 任意类型有可能是对象类型
a.b()
}
function fn2(a:unknown){
// unknown 未知类型, 不确定它是什么值
a.b()
// 报错:类型“unknown”上不存在属性“b”。
}
2.11 枚举类型
枚举类型是TypeScript添加到JavaScript的一项功能, 它允许描述一个值, 该值可能是一组可能的命名常量之一.
与大多数TypeScript功能不同,这不是对JavaScript的类型级添加, 而是添加到语言和运行时的东西.
详细内容稍后章节介绍