Typescript 当前(或是否有计划)支持 ?.
的 safe navigation 运算符
IE:
var thing = foo?.bar
// same as:
var thing = (foo) ? foo.bar : null;
此外,该运算符是否有更通用的名称(谷歌很难找到)。
property ?? property2
很好,但如果您尝试 property.company ?? property1.company
并且 property
为空,您会得到一个 NullReferenceException
v3.7
中添加的,称为 Optional Chaining
。有关代码示例,请参阅我的答案。
是的。 从 TypeScript 3.7(在 November 5, 2019 上发布)开始支持此功能,称为可选链接:
在其核心,可选链让我们编写代码,如果遇到 null 或 undefined,TypeScript 可以立即停止运行某些表达式。可选链接中的明星是新的?。用于可选属性访问的运算符。
有关详细信息,请参阅 TypeScript 3.7 release notes。
在 3.7 版之前,TypeScript 不支持此功能,但早在 Issue #16 on the TypeScript repo(可追溯到 2014 年)就已提出要求。
至于如何称呼这个运营商,似乎没有达成共识。除了“可选链接”(也称为 in JavaScript 和 Swift)之外,还有一些其他示例:
CoffeeScript 将其称为存在运算符(特别是存在运算符的“访问器变体”):
存在运算符 ? 的访问器变体。可用于吸收属性链中的空引用。使用它代替点存取器。在基值可能为空或未定义的情况下。
C# 将此称为空条件运算符。
只有当操作数的计算结果为非 null 时,空条件运算符才会将成员访问 ?. 或元素访问 ?[] 操作应用于其操作数;否则,它返回 null。
Kotlin 将其称为安全调用运算符。
可能还有很多其他的例子。
现在可以了,请参阅用户“甜甜圈”的回答。
旧答案:关于布尔运算符的标准 JavaScript 行为可能会有所帮助。布尔方法在比较对象时不返回 true 或 false,但在 OR 的情况下第一个等于 true 的值。
不如单个?,但它有效:
var thing = foo && foo.bar || null;
您可以根据需要使用任意数量的 &&:
var thing = foo && foo.bar && foo.bar.check && foo.bar.check.x || null;
默认值也是可能的:
var name = person && person.name || "Unknown user";
这是在 ECMAScript 可选链接规范中定义的,所以我们在讨论这个问题时可能应该参考可选链接。可能的实施:
const result = a?.b?.c;
总而言之,TypeScript 团队正在等待 ECMAScript 规范收紧,因此他们的实现在未来不会中断。如果他们现在实现了某些东西,那么如果 ECMAScript 重新定义他们的规范,最终将需要进行重大更改。
请参阅Optional Chaining Specification
在某些东西永远不会成为标准 JavaScript 的地方,TypeScript 团队可以按照他们认为合适的方式实现,但是对于未来的 ECMAScript 添加,即使他们提供早期访问权限,他们也希望保留语义,就像他们对许多其他功能所做的那样。
捷径
因此,所有 JavaScript 时髦的运算符都可用,包括类型转换,例如...
var n: number = +myString; // convert to number
var b: bool = !!myString; // convert to bool
手动解决方案
但回到问题。我有一个迟钝的例子来说明如何在 JavaScript(以及 TypeScript)中做类似的事情,尽管我绝对不是说它是一个优雅的功能,因为你真正追求的功能。
(foo||{}).bar;
因此,如果 foo
为 undefined
,则结果为 undefined
,并且如果 foo
已定义并且具有名为 bar
的属性且具有值,则结果就是该值。
我放了一个example on JSFiddle。
对于更长的示例,这看起来很粗略。
var postCode = ((person||{}).address||{}).postcode;
连锁功能
如果您在规范尚未制定时迫切需要更短的版本,我在某些情况下会使用这种方法。它评估表达式并在链无法满足或以 null/undefined 结束时返回默认值(注意 !=
在这里很重要,我们不想使用 !==
作为我们想要在这里进行一些积极的杂耍)。
function chain<T>(exp: () => T, d: T) {
try {
let val = exp();
if (val != null) {
return val;
}
} catch { }
return d;
}
let obj1: { a?: { b?: string }} = {
a: {
b: 'c'
}
};
// 'c'
console.log(chain(() => obj1.a.b, 'Nothing'));
obj1 = {
a: {}
};
// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));
obj1 = {};
// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));
obj1 = null;
// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));
(this.loop || {}).nativeElement
说 Property 'nativeElement' does not exist on type '{}'. any
this.loop
typeof angular.io/api/core/ElementRef
{}
,或 2)使用类型断言来使编译器静音。
foo
是一个实际有用的对象:(foo || {}).bar
通常不会在 typescript 中编译,因为 {}
与 foo
的类型不同。这就是@VeganHunter 的解决方案旨在避免的问题。
更新:是的,现在支持!
它刚刚与 TypeScript 3.7 一起发布:https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/
它被称为可选链接:https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/#optional-chaining
有了它:
let x = foo?.bar.baz();
相当于:
let x = (foo === null || foo === undefined) ?
undefined :
foo.bar.baz();
旧答案
在 github 上有一个开放的功能请求,您可以在其中表达您的意见/愿望:https://github.com/Microsoft/TypeScript/issues/16
2019 年 11 月 13 日编辑!
截至 2019 年 11 月 5 日,TypeScript 3.7 已经发布,它现在支持 ?.
可选的链式运算符🎉🎉🍾🍾🎉!!!
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining
仅用于历史目的:
编辑:感谢fracz评论,我已经更新了答案。
TypeScript 2.0 发布 !.
与?.
不同(C# 中的安全导航器)
有关更多详细信息,请参阅此答案:
https://stackoverflow.com/a/38875179/1057052
这只会告诉编译器该值不为空或未定义。这不会检查值是否为空或未定义。
TypeScript Non-null assertion operator
// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
// Throw exception if e is null or invalid entity
}
function processEntity(e?: Entity) {
validateEntity(e);
let s = e!.name; // Assert that e is non-null and access name
}
?
不同,因为它断言该值已定义。 ?
预计会静默失败/评估为假。无论如何,很高兴知道。
TypeScript 3.7 支持 Elvis (?.) 可选链接运算符。
您可以使用它来检查空值:如果猫为空或未定义,cats?.miows
将返回空。
您也可以将它用于可选方法调用:如果存在,cats.doMiow?.(5)
将调用 doMiow。
属性访问也是可能的:cats?.['miows']
。
参考:https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-beta/
?:
中。你有参考吗?
终于来了!
这里有一些例子:
// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()
// indexing
foo?.[0]
foo?.['bar']
// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()
但这与您的假设并不完全相同。
而不是评估
foo?.bar
对于这个我们都习惯写的小代码片段
foo ? foo.bar : null
它实际上评估为
(foo === null || foo === undefined) ?
undefined :
foo.bar
它适用于所有虚假值,如空字符串、0 或 false。
我只是没有解释他们为什么不将其编译为 foo == null
TypeScript 版本 2.0 不支持运算符 ?.
。
所以我使用以下功能:
export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
if (typeof someObject === 'undefined' || someObject === null)
return defaultValue;
else
return someObject;
}
用法如下所示:
o(o(o(test).prop1).prop2
另外,您可以设置默认值:
o(o(o(o(test).prop1).prop2, "none")
它与 Visual Studio 中的 IntelliSense 配合得非常好。
elvis<T>
;-)
我们在处理 Phonetradr 时创建了这个 util 方法,它可以让您使用 Typescript 对深层属性进行类型安全的访问:
/** * 对象深度属性的类型安全访问 * * @param obj 获取深度属性的对象 * @param unsafeDataOperation 返回深度属性的函数 * @param valueIfFail 如果没有此类属性,则返回值 * / 导出函数 getInSafe
return this.entry.fields.featuredImage.fields.file.url;
我通常不推荐这种方法(注意性能问题),但您可以使用扩展运算符浅克隆对象,然后您可以访问该对象上的属性。
const person = { personId: 123, firstName: 'Simon' };
const firstName = { ...person }.firstName;
这是有效的,因为“名字”的类型是“传播”的。
当我有一个可以返回 null 的 find(...)
表达式并且我需要它的单个属性时,我会最频繁地使用它:
// this would cause an error (this ID doesn't exist)
const people = [person];
const firstName2 = people.find(p => p.personId == 999).firstName;
// this works - but copies every property over so raises performance concerns
const firstName3 = { ...people.find(p => p.personId == 999) }.firstName;
打字稿推断类型的方式可能存在一些边缘情况,这不会编译,但这通常应该有效。
如前所述,目前仍在考虑中,但has been dead in the water到现在已经有几年了。
在现有答案的基础上,这是我能想到的最简洁的手动版本:
function val<T>(valueSupplier: () => T): T {
try { return valueSupplier(); } catch (err) { return undefined; }
}
let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(val(() => obj1.a.b)); // 'c'
obj1 = { a: {} };
console.log(val(() => obj1.a.b)); // undefined
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'
obj1 = {};
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'
obj1 = null;
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'
它只是在缺少属性错误时默默地失败。它回退到用于确定默认值的标准语法,也可以完全省略。
虽然这适用于简单的情况,但如果您需要更复杂的东西,例如调用函数然后访问结果的属性,那么任何其他错误也会被吞没。糟糕的设计。
在上述情况下,此处发布的其他答案的优化版本是更好的选择:
function o<T>(obj?: T, def: T = {} as T): T {
return obj || def;
}
let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(o(o(o(obj1).a)).b); // 'c'
obj1 = { a: {} };
console.log(o(o(o(obj1).a)).b); // undefined
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'
obj1 = {};
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'
obj1 = null;
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'
一个更复杂的例子:
o(foo(), []).map((n) => n.id)
您也可以换一种方式使用 Lodash' _.get()
之类的东西。简洁,但编译器将无法判断所用属性的有效性:
console.log(_.get(obj1, 'a.b.c'));
还没有(截至 2019 年 9 月),但自从“安全导航运算符”is now at Stage 3 以来,它正在 TypeScript 中实现。
观看此问题以获取更新:
https://github.com/microsoft/TypeScript/issues/16
几个引擎有早期的实现:
JSC:https://bugs.webkit.org/show_bug.cgi?id=200199
V8:https://bugs.chromium.org/p/v8/issues/detail?id=9553
SM:https://bugzilla.mozilla.org/show_bug.cgi?id=1566143
(通过 https://github.com/tc39/proposal-optional-chaining/issues/115#issue-475422578)
您现在可以安装一个插件来支持它:
npm install --save-dev ts-optchain
在您的 tsconfig.json 中:
// tsconfig.json
{
"compilerOptions": {
"plugins": [
{ "transform": "ts-optchain/transform" },
]
},
}
我希望这个答案在接下来的 6 个月左右会过时,但希望它同时对某人有所帮助。
我想这就是你要找的。 Powerbite 中的类似示例
/**
* Type-safe access of deep property of an object
*
* @param obj Object to get deep property
* @param unsafeDataOperation Function that returns the deep property
* @param valueIfFail Value to return in case if there is no such property
*/
export function getInSafe<O,T>(obj: O, unsafeDataOperation: (x: O) => T, valueIfFail?: any) : T {
try {
return unsafeDataOperation(obj)
} catch (error) {
return valueIfFail;
}
}
//Example usage:
getInSafe(sellTicket, x => x.phoneDetails.imeiNumber, '');
//Example from above
getInSafe(foo, x => x.bar.check, null);
_.get(obj, 'address.street.name')
非常适用于没有类型的 JavaScript。但是对于 TypeScript,我们需要真正的 Elvis 运算符!