文章目录
数据类型
主要分为原始数据类型和对象类型,原始类型有7个,string
、number
、bigint
、symbol
、boolean
、null
、undefined
。这些都是最底层实现的类型,叫原始类型是因为对这些类型的变量做操作,不会影响原始值,操作结果通常以返回的方式获取。
对象类型有,array
,tuple
,enum
数据类型的声明
首先知道 ,TS变量的声明往往要通过后置语法:
注解变量类型,这种就是显式声明变量:
let num: number = 1;
// 当然也可以和js一样写,ts会自己推导上下文来判断----类型推论(但并不是万能的)
let num = 1;
// 也可以注解多个类型
let num: number | undefined
TS在编译成JS的时候,会有静态类型检测。编译器就会通过类型注解去检测变量类型,不一致就会报出编译期错误。
注意,如果使用内置构造函数去创建变量类型,TS是会报错的,例如:
let str: string = new String('2') // 得到的是个object不符合设定的string所以报错
原始类型
string
// 字符串字面量
let str: string = 'wow'
// 显示类型转换
let str: string = String('wow')
// 模板字符串
let str: string = `wow`
number
let num: number = 6;
let num: number = Number(42);
let num: number = 3.14; /** 十进制浮点数 */
let num: number = 0b1010; /** 二进制整数 转成js会被转回十进制*/
let num: number = 0o744; /** 八进制整数 转成js会被转回十进制*/
let num: number = 0xf00d; /** 十六进制整数 转成js保持不变*/
let num: number = NaN;
let num: number = Infinity;
为啥二进制和八进制编译到es5回被转回十进制,我猜是因为二进制和八进制是es6的写法,es5本身不支持,所以会被转化。
bigint
number 表示的最大整数为 2^53 - 1,如果超过了这个界限,可以用 bigInt来表示,它可以表示任意大的整数,并且和number是不相等的,也就是不能一起运算和使用number的方法。
let bigNum: bigint = 9007199254740991n // 在一个整数字面量后加 n 的方式定义
const bigNum: bigint = BigInt(9007199254740991)
这个就先知道这么多就好,细节看这。
boolean
let bol: boolean = true;
let bol: boolean = new Boolean(1);
symbol
这个是es6新增的,表示值时唯一的。
let sym1: symbol = Symbol();
let sym2: symbol = Symbol('42');
let sym3: symbol = Symbol('42');
console.log(sym2 === sym3) // false
注意没有new Symbol()这种方式的创建方法, es6新增的取消了这种方式。
怎么用看这。
null和undefined
let nothing: null = null; // 好傻
let unde: undefined = undefined; //有点傻
undefined 和 null 是所有类型的子类型,可以赋值给其他类型的变量:
let num: number = undefined;
let u: undefined;
let num: number = u;
对象类型
array
需要指定子元素的类型(注意是非常严格的哦,比如往数字数组里push字符串编译会报错的):
/** 指定子元素是数字类型的数组 */
let num1: number[] = [1, 2, 3];
/** 指定子元素是字符串类型的数组 */
let num2: string[] = ['x', 'y', 'z'];
还可以用泛型定义数组类型,未来再学泛型。
/** 指定子元素是数字类型的数组 */
let num1: Array<number> = [1, 2, 3];
/** 指定子元素是字符串类型的数组 */
let num2: Array<string> = ['x', 'y', 'z'];
推荐第一种声明方式,能避免于JSX语法冲突,也能减少代码量。
之后还可以用接口表示数组和用类表示数组。
其实还有很多类数组,例如入参集合arguments,dom集合HTMLCollection等,他们有自己的api,不能和array混用。
tuple
这个是元组类型,最重要的特性就是可以限制数组元素的个数和类型。
let tuple: [string, number, boolean] = ["a", 2, false]; // 右侧类型写错或少赋值都会报错
let tuple: [string, number?, boolean?] = ["a"]; // 元素类型后缀一个 ? 来说明元素是可选的,可选元素必须在必选元素的后面,也就是如果一个元素后缀了 ?号,其后的所有元素都要后缀 ?号。
如果强行push元素的话,必须是[]里已注解的类型,但不能访问会报错,所以这样的操作不推荐。
元祖类型的使用可看这里的第四标题,基本上和数组类似。
enum
枚举类型,感觉就像是键值对的字典,每个键值对可以叫为一个枚举项。
enum Flag { success = 1, error = -1 }
enum Days = {sun, mon, tue, wed} // 不写=值的话,就默认等于索引值(像下标一样)
// 然后声明一个变量去拿里面的值,是枚举类型
let flag1: Flag = Flag.success // 1
let Days1: Days = Days.sun // 0
// 也可以直接拿取
console.log(Flag.success) // 1
console.log(Days[0]) // 0
特别的情况:
enum wow {a=3, b=2, c, d, e}
let A: wow = wow.a // 0
let B: wow = wow.b // 2
let C: wow = wow.c // 3 这个默认的索引值就为上一个值为开始算起+1,就算b为1.5也是加1成2.5
console.log(wow[3]) // 打印wow的值为3的居然打印出'c',是因为后者把前者a覆盖了
枚举项值如果不是数字就没有自动推导加1了,所以需要把每个枚举项都赋值了。
正向映射:就是正常的wow.a
查找。
方向映射:就是通过值去找key,wow[3]
。注意的是值为字符串枚举成员不会生成反向映射。
常数项:就是上面例子中的枚举项的样子。
计算所得项:就是枚举项中值是个计算语句,例如a='wow'.length
。
常数项和计算所得项的完整定义还是最好去看官方文档。
常数枚举:这种定义方式在编译后就会被删除,只留下枚举取出来的结果,能够减少转译后的es5代码量。且不能有计算所得项。
const enum Directions { // 通过const来定义
Up,
Down,
Left,
// Right = 'right'.length 加这个直接报错
}
可以试着加和不加const转译一下结果。
外部枚举:declare
定义的类型只会用于编译时的检查,编译后会被删除。
declare enum Directions {
Up,
Down,
Left,
Right
}
// 也可以
declare const enum Directions {...}
枚举合并:重复声明和两个对象合并一样。
enum Months {
Jan = 1,
Feb,
Mar,
Apr
}
enum Months {
May = 5,
Jun
}
console.log(Months.Apr) // 4
console.log(Months.Jun) // 6
枚举在项目中就用普通枚举就行了,方便运行时打印出来
object
表示非原始类型:
let obj: object
// 枚举类型
enum TokenType {
ACCESS = 'accessToken',
REFRESH = 'refreshToken'
}
obj = TokenType
obj = [1, 2, 3]
obj = [1, 'string'] // 元组类型
obj = { a: 1 }
obj = ()=> {}
可以看到枚举、数组、元组、返回空的函数和普通对象都是 object 类型,这种特性会引起一些问题,例如:
function Fn(obj: object) {
let obj1 = {...object} // ts会报错说obj1可能是一个{},这是因为obj1 = {...()=> {}}就是一个空对象
...
}
// 可以这样限制成真正的对象类型
function Fn(obj: { [key: string]: unknown }) {
let obj1 = {...object}
...
}
其他
any
任意类型,能绕过静态类型检测。
不好的地方是,你去获取any变量上不存在的属性和方法是不会报错的:
let anything: any = {};
anything.doAnything(); // 不会提示错误
anything = 'x'; // 不会提示错误
let num: number = anything; // 不会提示错误
除此之外,几乎其他非常规操作也不会报错。
再对比之前的array声明,我们可以这样定义就能和es5的数组一样了
let arr: any[] = ['1', 2, true]
变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:
let something; // 等同于let something: any;
something = 'seven';
something = 7;
any类型身上的任意子孙属性也都是any类型,会通过调用链传递类型。
当引入缺少类型注解的第三方库或者正在把JS应用改造成TS应用的时候,可以先把变量全部注解为any类型。平时在全TS的项目中开发的时候,要避免使用any类型,还要禁用隐式any。
nuknown
用来描述类型不确定的变量,可以被赋值任意类型的变量,但自己只能赋值给unknown和any类型。
let value: unknown
// value = true OK
// value = 10 OK
...
let value1: unknown = value // OK
let value2: any = value // OK
let value3: boolean = value // Error
...
它就像个严格版的any类型,例如:
function fn(obj: any) { // 可以传入任何类型
console.log(obj.name) // 不会提示错误
}
function fn(obj: unknown) { // 可以传入任何类型
console.log(obj.name) // 会提示错误
}
所以,只有当unknown被确定了类型后,才能使用相应类型的方法和属性,否者会报错。例如可以这样使用:
let result: unknown;
if (typeof result === 'number') {
result.toFixed(); // 此处 hover result 提示类型是 number,不会提示错误
}
前面说到过,unknown只能赋值给unknown和any类型,可能不是很理解在应用中是起什么作用的,举个例子:
function fn(obj: unknown) {
console.log(obj)
return obj
}
let obj: object = {}
obj = fn(obj) // 因为函数返回的是个unknown类型,所以会报错说不能将unknown赋予到object类型的变量上
void
就是表示没有返回值的函数,也可以说是表示没有返回任何类型。void和undefined之间有个怪异设计,这里就先不记录了。
function Fn():void{
console.log('fn')
}
// 如果是有返回类型的
function Fn():number{
return 123;
}
never
表示永远不会发生值的类型,never 是所有类型的子类型,它可以给所有类型赋值。
例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式;
function error(message: string): never {
throw new Error(message);
}