ES
- ?.可选链
- ??空值合并运算符
- _数字分隔符
- **指数操作符
TS
- ?: 可选参数和属性
- !非空断言操作符
- & 交叉类型
| 联合类型
可选链(optional Chaining)
可选链可让我们在查询具有多层级的对象时,不再需要进行冗余的各种前置校验
var info = user && user.info
又或是这种
var age = user&& user.info&&user.info.getAge&&user.info.getAge()
很容易命中 Uncaught TypeError:Cannot read property…
用了Optional Chaining,上面代码变成
var info = user?.info
var age = user?.info?.getAge?.()
TypeScript在尝试访问user.info前,会先尝试访问user,user既不是null也不是undefined才会继续往下访问,
如果user是null或者undefined,则表达式直接返回undefined
即可选链是一种先检查属性是否存在,再尝试访问该属性的运算符(?.)
目前,可选链支持以下语法操作:
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
??空值合并运算符 (Nullish coalescing Operator)
ES12 (ES2021)新增的特性,TypeScript 3.7支持了这个特性,当左侧的操作数为null或者undefined时,返回其右侧操作数,否则返回左侧操作数
// {
// level:null
// }
var level1 = user.level??"暂无等级" // level -> "暂无等级"
var level2 = user.other_level??"暂无等级" //level1 -> "暂无等级"
与逻辑或操作符(||)不同,||会在左侧操作数为falsy值(例如,’'或0)时返回右侧操作数,也就是说,如果使用||来为某些变量设置默认值,可能会遇到意料之外的行为:
// {
// level:0
// }
var level1 = user.level||'暂无等级'//leve1->暂无等级
var level2 = user.level ?? '暂无等级' //leve2 -> 0
?:可选参数和属性
TypeScript特有的,在TypeScript2.0支持这个特性,可选参数和属性会自动把undefined 添加到他们的类型中
即使他们的类型注解明确不包含undefined。例如,下面两个类型是完全相同的:
//使用--strictNullChecks参数进行编译
type T1 = (x?:number)=>string //x的类型是number | undefined
type T2 = (x?:number|undefined)=>string //x的类型是number | undefined
在TypeScript里,我们使用?:最多的情况是在接口中,通常:
interface Point{
x:number;
y:number;
}
let point:Point
point = {
x:1,
y:2
}
其中point中的两个属性都是必须的,如果赋值缺少任意一个就会报错:
point={
x:1
}
//Property 'y' is missing in type {x:number} but required in type Point
但接口里的属性不全都是必须的。有些是只在某些条件下存在,或者根本不存在。
所以,这里就需要可选属性(?.),即属性是可选的
interface Point{
x:number;
y:number;
z?:number;//可选属性
}
ley point:Point
point = {
x:1,
y:2
}
在TypeScript有两个内置的工具泛型可以帮助我们处理接口的可选操作:
Partial:把接口中的所有属性变成可选的
Required: 把接口中的所有可选属性改为必须的
Partial
Partial 的作用即把类型中的所有属性变成可选的
// Make all properties in T optional
type Partial<T>={
[P in keyof T]?:T[P]
}
例如:
interface Point{
x:number;
y:number;
}
type PartialPoint = Partial<Point>
//Partial 相当于:
type PartialPoint = {
x?:number;
y?:number;
}
//所有属性均可选
它具体是如何实现的呐
首先了解keyof,keyof 指的是把我们一个对象里面的键值对里的键(key)
一一罗列出来,并把他们联合起来形成一种联合类型
interface Point{
x:number;
y:number
}
type PointKeys = keyof Point //"x"|"y"
in 是遍历的作用, P in keyof T 把keyof T 进行一个个遍历并且每个都单独拿出来生成新的键值对
所以:
// Partial 语法
// type Partial<T>={
// [P in keyof T]?:T[P]
// }
interface Point {
x:number;
y:number;
}
type PartialPoint = Partial<Point>
//第一步
type PartialPoint = {
[P in 'x'|'y']?:Point[P]
}
//第二步
type PartialPoint = {
x?:Point['x'];
y?:Point['y']
}
// 最终
type PartialPoint = {
x?:number;
y?:number
}
因此,实现了Partial的效果
Required
Required的作用刚好与Partial相反,就是将接口中所有可选属性改为必须的,区别就是把Partial里面的?替换成了-?
type Required<T>={
[P in keyof T]-?:T[P];
}
例如:
interface Point{
x?:number;
y?:number;
}
type RequiredPoint = Required<Point>
// RequiredPoint 相当于:
// type RequiredPoint = {
// x:number;
// y:number;
// }
// 所有属性均必须
!非空断言操作符
TypeScript特有的 在TypeScript2.0支持了这个特性,在上下文中当类型检查器无法判定类型时,
一个新的后缀表达式操作符!可以用于断言操作对象是非null 和非undefined类型的。具体而言,运算x!产生一个不包含null和undefined的x的值。
function sayHello(hello:string|undefined){
const hi1 = hello!.toLowerCase() //Ok
const hi2 = hello.toLowerCase() //Error: Object is possibly 'undefined'
}
仅仅只是骗过了编译器,当你调用sayHello()依然会报错,这样使用时因为你已经断言了hello一定会是string
let root:(HTMLElement|null)=document.getElementById('root')
//非空断言操作符->这样写只是为了骗过编译器,防止编译的时候报错,但打包后的代码可能还是会报错
root!.style.color = "red"
非空断言操作符与类型守卫
类型首位用于确保该类型在一定范围内,常用typeof,instanceof,in等
function sayHello(hello:string|undefined){
if(typeof hello === 'string'){
const hi = hello.toLowerCase()
}
}
但如果你这样写:
function sayHello(hello:string|undefined){
const isSay = typeof hello === "string"
if(isSay){
const h1 = hello.toLowerCase() //Error:Object is possibly "undefined"
const h2 = hello!.toLowerCase() //OK
}
}
就会报错,即使isSay被分配到了类型守卫 TypeScript也只会丢失该信息。所以我们一般会const hi = hello!.toLowerCase() 加上非空断言操作符
但TypeScript4.4RC会修复这个问题,如果你遇到这个问题,可升级到TypeScript4.4版本后
_数字分隔符(Numberic separators)
ES12 (ES2021)新增的特性,TypeScript2.7就已经支持了这个特性,这个特性允许用户在数字之间使用下划线_来对数字分组。
const million = 1_000_000
const phone = 173_1777_777
const bytes =0xFF_OA_B3_F2
const word = 0b1100_0011_1101_0001
需要注意的是以下函数是不支持分隔符:
Number()
parseInt()
parseFloat()
const million = “1_234_567”
Number(million) //NaN
parseInt(million) //1
parseFloat(million) //1
指数操作符
ES7(ES2016)新增的特性
2**5 //32
交叉类型(intersection Types)
在TypeScript中交叉类型是将多个类型合并为一个类型 我们可以通过&把现有的多种类型叠加到一起成为一种类型
它包含了所需的所有类型的特性
type PointX = {
x:number
}
type Point = PointX&&{
y:number
}
let point:Point = {
x:1,
y:2
}
如果多个类型存在相同的属性呢
type PointX ={
x:number;
z:string;
}
type Point = PointX &{
y:number;
z:number
}
let point:Point = {
x:1,
y:2,
z:3 //Type ‘number’ is not assignable to type ‘never’
}
这里的z为什么是never类型呢?因为string&number的值是永不存在的值,即never
type PointX = {
x:number;
z:{x:string}
}
type Point = PointX &{
y:number;
z:{z:number}
}
let point:Point={
x:1,
y:2,
z:{
x:‘1’,
z:2
}
}
而这样是可以的,所以 即多个类型合并为一个交叉类型时,如果多个类型间存在
同名基础类型属性,合并后的同名基础类型属性为never,如果同名属性均为非基础类型,
则可以合并
| 联合类型(Union Types)
联合类型表示一个值可以是几种类型之一,用竖线(|)分割每个类型,
所以number|string|boolean表示一个值可以是number,string,boolean
let user:string|number|boolean = “an”
联合类型通常与null 或undefined一起使用
const helloName = (name:string|undefined){
// …
};
你也可以这么用:
type Hello = “say”|‘kiss’|‘smile’