这篇文章的由来呢,是当时在写一个 Story 的时候,因为一个 any
类型的 object
,由于无法直观地得出它是否是 undefined
,所以报了很多次错。于是,开启万能的 google,搜了一把,然后就得到了一个词:Optional Chaining.
于是乎,有了这篇文章。
Of course,这篇文章并不是我最初的第一版,也是我经过一番修缮才放出来的。同时,这篇文章中的内容,是我结合 TS 的官方文档及网上的一些博客综合而来,然后加上我自己的一些理解及例程,最终完成的,如有理解错误之处,还望指出!
下面,开始关于 Optional Chaining 的正文。
Optional Chaining 的介绍
Optional Chaining ,在代码中的使用形式呢,是这样的➡️ ?.
?.
使我们可以在遇到 null
或 undefined
时立即停止运行某些表达式。
那么,怎么用呢?来看一个简单的例子:
const x = foo?.bar.baz();
上面这行代码是什么意思呢?即:⬇️
- 如果
foo
定义了,即不为null
或undefined
时,则继续执行const x = foo.bar.baz();
- 如果
foo
为null
或undefined
时,返回undefined
,即执行const x = undefined;
这么说我觉得已经是相当容易理解了吧!!!😊当然,如果将以上这段代码转换成比较熟悉的形式,可能会更容易理解一些!!!⬇️
const x = (foo !== null && foo !== undefined)
? foo.bar.baz()
: undefined;
以上这段代码,我又再一次觉得,相当简单了!!!😁
Of course,如果还是有不理解的👬,那么,我😶
还有一个需要注意的点(上面忘说了😂)
需要注意的是:
?.
只能检查左侧的变量或属性。
如果以下这段代码这种的形式,则依旧会出错:
const foo = { ba: "test" };
const x = foo?.bar.bar;
?.
只检查了左侧的 foo
,并没有检查右侧的 bar
。
😂😂😂
如果想要改成正确的形式,可以写成以下这种形式:
const x = foo?.bar?.bar;
再次 Of Course,自我感觉这种好麻烦啊,如果我有一个 object ,它的属性及属性的属性及属性的属性的属性。。。都无法确定是不是 undefined
,我岂不是要写成这种:const result = a?.b?.c?.d?.e?.f?.g;
😂😂😂
给官方跪了 Orz Orz Orz Orz …
难道不能用一个声明作用的符号来声明这一整行都需要 Check 吗?或者,直接将这个功能内置成默认的!
当然了,以上只是我个人的想法,毕竟天大地大官方最大😂而且,如果真的有人将代码写成 a?.b?.c?.d?.e?.f?.g
这种,我想说:👬,可以考虑换行了😂
这里是个分隔线,真的没什么内容
我总感觉忘了啥,但是想不起来了😂
上面的内容算是一个简单的介绍,继续往下看吧,骚年
⬇️
⬇️⬇️
⬇️⬇️⬇️
⬇️⬇️⬇️⬇️
⬇️⬇️⬇️⬇️⬇️
Optional Chaining 的好处
好处呢,其实我觉得看了上面的内容后,对 ?.
的好处应该已经知道个大概了。
不过,毕竟做了笔记的,还是列出来吧!别嫌我啰嗦😂
有一段代码,是用以前的(多久以前?我也不知道,反正就是 ?.
没出的时候) TS 语法写的,是这样的:
if (foo && foo.bar && foo.bar.baz()) {
// ...
}
还有一段代码,是用现在的(我也不知道读者您们的阅读时间,总之就是 ?.
出来了一段时间,但是也没多久,大概 TS 3.7.4
版本的时间)语法写的,是这样的:
if (foo?.bar?.baz()) {
// ...
}
比较完这两段代码,是不是一下就清晰明了了!
是不是恍然大悟!
是不是茅塞顿开!
是不是立地成佛!
好吧,就是变简单了!没之前那么长了!
这里。。。
我再加一个需要注意的点(临时起意😂)
毕竟,有些东西得一点点引出来,对吧?😁
那就是:
?.
不同于 &&
可能直接这样写出来有些不太直观,那就先来看一个例子:
"" && 1 // 0
""?.concat("hello") // "hello"
?.
判断的是是否为 null
或 undefined
而 &&
判断的是是否为 flase
感觉上,似乎还是有些莫名其妙哈😂,不过呢,能理解最好,不能理解也无所谓,反正这条不是很重要,基本上在写代码的过程中属于自然而然就懂的那种…
下面呢,是 ?.
的几个用法
首先,第一个是可选属性访问
可选属性访问是什么东西呢?就是说,?.
这个东西可以用来对 object
进行检查,检查什么呢?检查这个 object
是不是 null
或 undefined
,如果是 null
或 undefined
,那么就代表这个 object
的属性是不存在的;反之,如果这个object
不是 null
或 undefined
,那么就代表这个 object
的属性是可以取出来的。
这个就是可选。
当然了,在上面的解释中呢,有一点需要注意,不知道有没有看出来的,而且在前面的部分中,我有提到过,那就是:
?.
只检查左边的 object
,也就是说,foo?.bar
这样的形式,如果 foo
为 null
或 undefined
,则 foo?.bar
在 foo
这里就直接返回了,根本不会走到 .bar
这一步;而如果 foo
不为 null
或 undefined
,则可以取 foo.bar
。
当然了,还有一个需要注意的小点,如果 foo
不为空,但它没有 bar
这个属性,那么 foo.bar
取出来的值为 undefined
。
好,言归正传,让我们来看一个例子,实际来看一下可选属性访问 的效果:
// 第一种情况,person 存在
const printCity = (person?: any) => {
console.log(person?.name);
};
const person = {
name: "Jack"
};
printCity(person);
// 执行 ts-node optionalPropertyAccesses.ts
Jack
// 第二种情况,person 不存在
const printCity = (person?: any) => {
console.log(person?.name);
};
printCity();
// 执行 ts-node optionalPropertyAccesses.ts
undefined
以上两种情况呢,就是 ?.
的效果了。
当然,让大家看一下如果没有使用 ?.
的情况:
// object 不存在,也没有使用 ‘?.’ ,直接报错
const printCity = (person?: any) => {
console.log(person.name);
};
printCity();
// 执行 ts-node optionalPropertyAccesses.ts
/Users/xxx/xxx/optionalChaining/optionalPropertyAccesses.ts:2
console.log(person.name);
^
TypeError: Cannot read property 'name' of undefined
at printCity (/Users/xxx/xxx/optionalChaining/optionalPropertyAccesses.ts:2:24)
at Object.<anonymous> (/Users/xxx/xxx/optionalChaining/optionalPropertyAccesses.ts:9:1)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Module.m._compile (/Users/xxx/xxx/node_modules/ts-node/src/index.ts:536:23)
at Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Object.require.extensions.(anonymous function) [as .ts] (/Users/xxx/xxx/node_modules/ts-node/src/index.ts:539:12)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
当然,下面我也把上面提到的小注意点列出来,看一下:
// object 存在,属性不存在,直接为 undefined
const printCity = (person?: any) => {
console.log(person.city);
};
const person = {
name: "Jack"
};
printCity(person);
// 执行 ts-node optionalPropertyAccesses.ts
undefined
下面是第二个用法:可选元素访问
可选元素访问又是什么意思呢?
就是可以用来从数组中取元素,即 ?.
可以用来检查数组是否存在。
话不多说,直接上示例代码吧:
// 第一种情况,数组存在
const printFourthElement = (array?: string[]) => {
console.log(array?.[3]);
};
const array1 = ["Jack", "Tom", "Bob", "Jeny"];
printFourthElement(array1);
// 执行 ts-node optionalElement.ts
Jeny
// 第二种情况,数组不存在
const printFourthElement = (array?: string[]) => {
console.log(array?.[3]);
};
printFourthElement();
// 执行 ts-node optionalElement.ts
undefined
还是一样,如果没有使用 ?.
,则会报错:
// 数组不存在,没有使用 ‘?.’ ,直接报错
const printFourthElement = (array?: string[]) => {
console.log(array[3]);
};
printFourthElement();
// 执行 ts-node optionalElement.ts
/Users/xxx/xxx/optionalChaining/optionalElement.ts:2
console.log(array[3]);
^
TypeError: Cannot read property '3' of undefined
at printFourthElement (/Users/xxx/xxx/optionalChaining/optionalElement.ts:2:22)
at Object.<anonymous> (/Users/xxx/xxx/optionalChaining/optionalElement.ts:7:1)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Module.m._compile (/Users/xxx/xxx/node_modules/ts-node/src/index.ts:536:23)
at Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Object.require.extensions.(anonymous function) [as .ts] (/Users/xxx/xxx/node_modules/ts-node/src/index.ts:539:12)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
再接着,是第三种用法:可选调用
我相信,看了好多个🌰了,大家已经能猜出来可选调用是什么意思了。
没错,就是方法的调用。
上吧,皮卡丘!
// 第一种情况,该方法(箭头函数...等)定义了
const printMessage = (convert?: (message: string) => string) => {
console.log(convert?.("hello world"));
};
const convert = (message: string): string => message.toUpperCase();
printMessage(convert);
// 执行 ts-node optionalCall.ts
HELLO WORLD
// 第二种情况,该方法(箭头函数...等)没有定义
const printMessage = (convert?: (message: string) => string) => {
console.log(convert?.("hello world"));
};
printMessage();
// 执行 ts-node optionalCall.ts
undefined
// 没有使用 '?.' 的情况,直接报错
const printMessage = (convert?: (message: string) => string) => {
console.log(convert("hello world"));
};
printMessage();
// 执行 ts-node optionalCall.ts
/Users/xxx/xxx/optionalChaining/optionalCall.ts:2
console.log(convert("hello world"));
^
TypeError: convert is not a function
at printMessage (/Users/xxx/xxx/optionalChaining/optionalCall.ts:2:17)
at Object.<anonymous> (/Users/xxx/xxx/optionalChaining/optionalCall.ts:7:1)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Module.m._compile (/Users/xxx/xxx/node_modules/ts-node/src/index.ts:536:23)
at Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Object.require.extensions.(anonymous function) [as .ts] (/Users/xxx/xxx/node_modules/ts-node/src/index.ts:539:12)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
下面这个呢,其实不算是用法,顶多算一个注意点
好吧,再加一个注意点:Optional Chaining 不会中断其他表达式
什么意思呢?就是说 ?.
值作用于可选属性访问、可选元素访问、可选调用。
如果 ?.
使用在某一个表达式内,那这个表达式肯定是会被检查的;
但另一个没有使用 ?.
的表达式,是不会受这个表达式中 ?.
的影响的。
话不多说,来看代码:
const printFourthElement = (array?: string[]) => {
console.log(array?.[3].concat(", how are you?"));
console.log(array?.[3] + ", how are you?");
};
const array1 = ["Jack", "Tom", "Bob", "Jeny"];
printFourthElement(array1);
printFourthElement();
// 执行结果----------------------
Jeny, how are you?
Jeny, how are you?
undefined
undefined, how are you? // 重点看这个
好,基本上就这么多,然后呢,这个功能是TS 3.7
版本后出来的
如果要使用这个功能,请确保版本在 3.7
以上。
有时会不起作用,那肯定是没配置好,不过,其实真的不需要配置什么,package.json
改个版本号就完了。😂
或者原版本升下级,卸了重装?随便!实在不行,IDE 关了重启?