盤點 TypeScript 中我們常用的那些神奇符號
回復(fù)交流,加入前端編程面試算法每日一題群

?. 可選鏈(Optional Chaining)
ES11(ES2020)新增的特性,TypeScript 3.7 支持了這個特性
我們在 為什么要使用 TypeScript?TypeScript 相對于 JavaScript 的優(yōu)勢是什么?中提到 TypeScript 與標準同步發(fā)展,并推進了很多 ECMAScripts 語法提案,比如可選鏈操作符(
?.)、空值合并操作符(??)、Throw 表達式、正則匹配索引等,所以,這里介紹的符號大部分都是 ECMAScripts 規(guī)范的,TypeScript 特有的只有?:、!、&、|
可選鏈可讓我們在查詢具有多層級的對象時,不再需要進行冗余的各種前置校驗:
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 才會繼續(xù)往下訪問,如果user是 null 或者 undefined,則表達式直接返回 undefined
即可選鏈是一種先檢查屬性是否存在,再嘗試訪問該屬性的運算符 ( ?. )
目前,可選鏈支持以下語法操作:
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
?? 空值合并運算符(Nullish coalescing Operator)
ES12(ES2021)新增的特性,TypeScript 3.7 支持了這個特性,當(dāng)左側(cè)的操作數(shù)為 null 或者 undefined 時,返回其右側(cè)操作數(shù),否則返回左側(cè)操作數(shù)。
// {
// "level": null
// }
var level1 = user.level ?? '暫無等級' // level1 -> '暫無等級'
var level2 = user.other_level ?? '暫無等級' // level1 -> '暫無等級'
與邏輯或操作符(||) 不同,|| 會在左側(cè)操作數(shù)為 falsy 值(例如,'' 或 0)時返回右側(cè)操作數(shù)。也就是說,如果使用 || 來為某些變量設(shè)置默認值,可能會遇到意料之外的行為:
// {
// "level": 0
// }
var level1 = user.level || '暫無等級' // level1 -> 暫無等級
var level2 = user.level ?? '暫無等級' // level2 -> 0
?: 可選參數(shù)和屬性
TypeScript 特有的,在 TypeScript 2.0 支持了這個特性,可選參數(shù)和屬性會自動把 undefined 添加到他們的類型中,即使他們的類型注解明確不包含 undefined 。例如,下面兩個類型是完全相同的:
// 使用--strictNullChecks參數(shù)進行編譯
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 中的兩個屬性 x 、 y 都是必須的,如果賦值時缺少任意一個就會報錯:
point = {
x: 1
}
// Property 'y' is missing in type '{ x: number; }' but required in type 'Point'.
但接口里的屬性不全都是必需的。有些是只在某些條件下存在,或者根本不存在。所以,這里就需要可選屬性( ?. ),即屬性是可選的
interface Point {
x: number;
y: number;
z?: number; // 可選屬性
}
let point: Point
point = {
x: 1,
y: 2
}
在 TypeScript 有兩個內(nèi)置的工具泛型可以幫助我們處理接口的可選操作:
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>
// PartialPoint 相當(dāng)于:
// type PartialPoint = {
// x?: number;
// y?: number;
// }
// 所有屬性均可選
它具體是如何實現(xiàn)的喃?
首先了解 keyof ,keyof 指的是把我們一個對象里面的鍵值對里的鍵( key )一一羅列出來,并把它們聯(lián)合起來形成一種聯(lián)合類型:
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;
}
因此,實現(xiàn)了 Partial 的效果
Required
Required 的作用剛好與 Partial 相反,就是將接口中所有可選的屬性改為必須的,區(qū)別就是把 Partial 里面的 ? 替換成了 -?
type Required<T> = {
[P in keyof T]-?: T[P];
}
例如:
interface Point {
x?: number;
y?: number;
}
type RequiredPoint = Required<Point>
// RequiredPoint 相當(dāng)于:
// type RequiredPoint = {
// x: number;
// y: number;
// }
// 所有屬性均必須
! 非空斷言操作符
TypeScript 特有的,在 TypeScript 2.0 支持了這個特性,在上下文中當(dāng)類型檢查器無法斷定類型時,一個新的后綴表達式操作符 ! 可以用于斷言操作對象是非 null 和非 undefined 類型的。具體而言,運算 x! 產(chǎn)生一個不包含 null 和 undefined 的 x 的值。
function sayHello(hello: string | undefined) {
const hi1 = hello!.toLowerCase() // OK
const hi2 = hello.toLowerCase() // Error: Object is possibly 'undefined'
}
僅僅只是騙過了編譯器,當(dāng)你調(diào)用 sayHello() 依然會報錯,這樣使用是因為你已經(jīng)斷言了 hello 一定是 string
let root: (HTMLElement | null) = document.getElementById('root')
// 非空斷言操作符--> 這樣寫只是為了騙過編譯器,防止編譯的時候報錯,但打包后的代碼可能還是會報錯
root!.style.color = 'red'
非空斷言操作符 與 類型守衛(wèi)
類型守衛(wèi)用于確保該類型在一定的范圍內(nèi),常用 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 hi1 = hello.toLowerCase() // Error: Object is possibly 'undefined'.
const hi2 = hello!.toLowerCase() // OK
}
}
就會報錯,即使 isSay 被分配到了類型守衛(wèi)值,TypeScript 也只會丟失該信息。所以我們一般會 const hi = hello!.toLowerCase() 加上非空斷言操作符
但 TypeScript 4.4 RC 會修復(fù)這個問題,如果你遇到這個問題,可升級到 TypeScript 4.4 版本后
_ 數(shù)字分隔符(Numeric separators)
ES12(ES2021)新增的特性,TypeScript 2.7 就已經(jīng)支持了這個特性, 這個特性允許用戶在數(shù)字之間使用下劃線_來對數(shù)字分組。
const million = 1_000_000
const phone = 173_1777_7777
const bytes = 0xFF_0A_B3_F2
const word = 0b1100_0011_1101_0001
需要注意的是以下函數(shù)是不支持分隔符:
Number()parseInt()parseFloat()
const million = '1_234_567'
Number(million)
// NaN
parseInt(million)
// 1
parseFloat(million)
// 1
** 指數(shù)操作符
ES7(ES2016)新增的特性
2**5 // 32
& 交叉類型(Intersection Types)
在 TypeScript 中,交叉類型是將多個類型合并為一個類型,我們可以通過 & 把現(xiàn)有的多種類型疊加到一起成為一種類型,它包含了所需的所有類型的特性
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
},
}
而這樣是可以的,所以,即多個類型合并為一個交叉類型時,如果多個類型間存在同名基礎(chǔ)類型屬性時,合并后的同名基礎(chǔ)類型屬性為 never ,如果同名屬性均為非基礎(chǔ)類型,則可以成功合并
| 聯(lián)合類型(Union Types)
聯(lián)合類型表示一個值可以是幾種類型之一,用豎線( |)分隔每個類型,所以 number | string | boolean 表示一個值可以是 number, string,或 boolean
let user: string | number | boolean = 'an'
聯(lián)合類型通常與 null 或 undefined 一起使用:
const helloName = (name: string | undefined) => {
/* ... */
};
你也可以這么用:
type Hello = 'say' | 'kiss' | 'smile';
來源 | https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/529
最后
號內(nèi)回復(fù):
120 套模版