495

Typescript 目前(或是否有计划)支持安全导航操作符?.

IE:

var thing = foo?.bar
// same as:
var thing = (foo) ? foo.bar : null;

此外,该运算符是否有更通用的名称(谷歌很难找到)。

4

15 回答 15

287

是的。从 TypeScript 3.7(2019 年 11 月 5 日发布)开始,支持此功能,称为Optional Chaining

在其核心,可选链让我们编写代码,如果遇到 a nullor ,TypeScript 可以立即停止运行某些表达式undefined。可选链中的明星是可选属性访问?.的新操作符。

有关更多详细信息,请参阅TypeScript 3.7 发行说明


在 3.7 版之前,TypeScript 不支持此功能,尽管早在 TypeScript 存储库的第 16 期(可追溯到 2014 年)中就提出了要求。

至于如何称呼这个运营商,似乎还没有达成共识。除了“可选链接”(这也是它在 JavaScript 中的名称)之外,还有一些其他示例:

  • CoffeeScript 将其称为存在运算符(特别是存在运算符的“访问器变体”):

存在运算符的访问器变体?.可用于吸收属性链中的空引用。.在基值可能为nullundefined的情况下,使用它代替点访问器。

仅当操作数的计算结果为非 null 时,空条件运算符才将成员访问?.、或元素访问、操作应用于其操作数;?[]否则,它返回null

可能还有很多其他的例子。

于 2013-03-07T00:21:21.980 回答
154

现在可以了,请参阅用户“甜甜圈”的回答。

旧答案:关于布尔运算符的标准 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";
于 2013-03-08T12:34:45.250 回答
115

这是在 ECMAScript 可选链接规范中定义的,所以我们在讨论这个问题时可能应该参考可选链接。可能的实施:

const result = a?.b?.c;

总而言之,TypeScript 团队正在等待 ECMAScript 规范收紧,因此他们的实现在未来不会中断。如果他们现在实现了某些东西,那么如果 ECMAScript 重新定义他们的规范,最终将需要进行重大更改。

请参阅可选的链接规范

在某些东西永远不会成为标准 JavaScript 的地方,TypeScript 团队可以按照他们认为合适的方式实现,但是对于未来的 ECMAScript 添加,即使他们提供早期访问权限,他们也希望保留语义,就像他们对许多其他功能所做的那样。

捷径

因此,所有 JavaScript 时髦的运算符都可用,包括类型转换,例如...

var n: number = +myString; // convert to number
var b: bool = !!myString; // convert to bool

手动解决方案

但回到问题。我有一个迟钝的例子来说明如何在 JavaScript(以及 TypeScript)中做类似的事情,尽管我绝对不是说它是一个优雅的功能,因为你真正追求的功能。

(foo||{}).bar;

因此,如果foo结果undefinedundefined并且如果foo已定义并且具有名为的属性,则该属性bar具有值,结果就是该值。

在 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'));
于 2013-03-07T06:27:46.573 回答
107

更新:是的,现在支持!

它刚刚与 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

于 2015-02-12T04:53:45.363 回答
41

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#中的Safe Navigator)不一样

有关更多详细信息,请参阅此答案:

https://stackoverflow.com/a/38875179/1057052

这只会告诉编译器该值不为空或未定义。这不会检查值是否为空或未定义。

TypeScript 非空断言运算符

// 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
}
于 2016-11-01T13:27:37.813 回答
16

TypeScript 3.7 支持 Elvis (?.) 可选链接运算符。

您可以使用它来检查 null 值:cats?.miows如果 cat 为 null 或未定义,则返回 null。

您也可以将其用于可选方法调用:cats.doMiow?.(5)如果存在 doMiow,将调用它。

财产访问也是可能的:cats?.['miows']

参考:https ://devblogs.microsoft.com/typescript/announcing-typescript-3-7-beta/

于 2019-10-02T15:45:24.713 回答
12

终于来了!

这里有一些例子:

// 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

于 2019-11-12T06:44:49.260 回答
10

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 配合得非常好。

于 2017-01-17T01:50:25.753 回答
3

我们在使用Phonetradr时创建了这个 util 方法,它可以让您使用 Typescript 对深层属性进行类型安全的访问:

/**
 * 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);

于 2017-07-28T15:15:43.693 回答
2

我通常不推荐这种方法(注意性能问题),但您可以使用扩展运算符浅克隆对象,然后您可以访问该对象上的属性。

 const person = { personId: 123, firstName: 'Simon' };
 const firstName = { ...person }.firstName;

这是有效的,因为“名字”的类型是“传播”的。

当我有一个find(...)可以返回 null 的表达式并且我需要它的单个属性时,我会最频繁地使用它:

 // 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;

打字稿推断类型的方式可能存在一些边缘情况,这不会编译,但这通常应该有效。

于 2018-11-12T07:33:10.330 回答
2

它被称为可选链接,它在Typescript 3.7中

可选链接让我们编写代码,如果遇到 null 或 undefined,我们可以立即停止运行某些表达式

于 2019-11-01T13:41:12.493 回答
0

正如之前回答的那样,它目前仍在考虑中,但它已经死在水中几年了。

在现有答案的基础上,这是我能想到的最简洁的手动版本:

jsfiddle

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'

它只是在缺少属性错误时默默地失败。它回退到用于确定默认值的标准语法,也可以完全省略。


虽然这适用于简单的情况,但如果您需要更复杂的东西,例如调用函数然后访问结果的属性,那么任何其他错误也会被吞没。糟糕的设计。

在上述情况下,此处发布的其他答案的优化版本是更好的选择:

jsfiddle

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'));
于 2018-04-19T19:56:23.127 回答
0

还没有(截至 2019 年 9 月),但由于“安全导航运算符”现在处于第 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 个月左右会过时,但希望它同时对某人有所帮助。

于 2019-09-30T15:55:57.967 回答
0

我想这就是你要找的。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);
于 2021-05-03T07:36:28.060 回答
-1

_.get(obj, 'address.street.name')适用于没有类型的 JavaScript。但是对于 TypeScript ,我们需要真正的 Elvis 运算符!

于 2019-04-20T02:17:10.003 回答