在 TS 中你踩过默认值的坑么?给 ?? 运算符把坑填了!
一、一个简单的组件
让我们从一个简单的组件开始:
type Person = {
name: string,
subscription?: Subscription
};
type Subscription = {
fee: number
};
type Props = {
person: Person
};
const PersonCard: React.FC<Props> = ({
person
}) => {
return (
<div>
<div>
<span>姓名: </span>
<span>{person.name}</span>
</div>
<div>
<span>订阅费用: </span>
<span>
{person.subscription?.fee}
</span>
</div>
</div>
);
};
在上面的 PersonCard
组件中,我们使用了 person.subscription?.fee
可选链,以避免在显示订阅费用时出现错误。但是,假设我们想要在该用户没有开通订阅功能的情况下,显示 "尚未开通订阅"。要实现上述功能,我们可能会使用以下的代码:
<span>
{person.subscription?.fee ||
"尚未开通订阅"}
</span>
这看起来好像没有什么问题,但是如果当前用户有开通订阅功能,只是订阅费用为 0,即 person.subscription?.fee
的值是 0,在这种情况下,页面上将显示 "尚未开通订阅",这是因为 0
是一个 falsy 值。那如何解决这个问题呢?答案就是可以使用 TypeScript 3.7 版本提供的空值合并运算符(??)。
二、空值合并运算符
空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。
与逻辑或(||)操作符不同,逻辑或会在左操作数为 falsy 值时返回右侧操作数。也就是说,如果你使用 || 来为某些变量设置默认的值时,你可能会遇到意料之外的行为。比如为 falsy 值(''、NaN 或 0)时。
下面我们来看一个具体的例子:
const foo = null ?? 'default string';
console.log(foo); // 输出:"default string"
const baz = 0 ?? 42;
console.log(baz); // 输出:0
以上 TS 代码经过编译后,会生成以下 ES5 代码:
"use strict";
var _a, _b;
var foo = (_a = null) !== null && _a !== void 0 ? _a : 'default string';
console.log(foo); // 输出:"default string"
var baz = (_b = 0) !== null && _b !== void 0 ? _b : 42;
console.log(baz); // 输出:0
通过观察以上代码,我们更加直观的了解到,空值合并运算符是如何解决前面 ||
运算符存在的潜在问题。下面我们来继续介绍空值合并运算符的特性和使用时的一些注意事项。
三、短路
当空值合并运算符的左表达式不为 null
或 undefined
时,不会对右表达式进行求值。
function A() { console.log('A was called'); return undefined;}
function B() { console.log('B was called'); return false;}
function C() { console.log('C was called'); return "foo";}
console.log(A() ?? C());
console.log(B() ?? C());
上述代码运行后,控制台会输出以下结果:
A was called
C was called
foo
B was called
false
四、不能与 && 或 || 操作符共用
若空值合并运算符(??)直接与 AND(&&)和 OR(||)操作符组合使用 ?? 是不行的。这种情况下会抛出 SyntaxError 。
// '||' and '??' operations cannot be mixed without parentheses.
null || undefined ?? "foo"; // raises a SyntaxError
// '&&' and '??' operations cannot be mixed without parentheses.
true && undefined ?? "foo"; // raises a SyntaxError
但当使用括号来显式表明优先级时是可行的,比如:
(null || undefined ) ?? "foo"; // 返回 "foo"
五、与可选链操作符 ?. 的关系
空值合并操作符针对 undefined 与 null 这两个值,可选链式操作符(?.) 也是如此。可选链式操作符,对于访问属性可能为 undefined 与 null 的对象时非常有用。
interface Customer {
name: string;
city?: string;
}
let customer: Customer = {
name: "Semlinker"
};
let customerCity = customer?.city ?? "Unknown city";
console.log(customerCity); // 输出:Unknown city
前面我们已经介绍了空值合并运算符的应用场景和使用时的一些注意事项,该运算符不仅可以在 TypeScript 3.7 以上版本中使用,你也可以在 JavaScript 的环境中使用它,但你需要借助 Babel,在 Babel 7.8.0 版本也开始支持空值合并运算符。
六、参考资源
nullish-coalescing-with-react-and-typescript MDN - Nullish_coalescing_operator 深入理解 TypeScript - nullish-coalescing