盘点TypeScript中我们常用的那些神奇符号

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’

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值