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

ie:

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

此外,这个操作符是否有更常见的名称(很难为谷歌)。


当前回答

是的。从TypeScript 3.7(2019年11月5日发布)开始,这个特性就被支持了,被称为可选链接:

在其核心,可选的链接让我们编写的代码中,TypeScript可以立即停止运行一些表达式,如果我们遇到一个null或未定义。可选连锁的明星是新的?操作符用于可选属性访问。

更多细节请参考TypeScript 3.7发布说明。


在3.7版本之前,TypeScript中不支持这一点,尽管早在TypeScript回购的第16个问题(可以追溯到2014年)中就已经请求过了。

至于如何称呼这个运算符,似乎还没有达成共识。除了“可选链接”(这也是它在JavaScript和Swift中被称为的),还有其他一些例子:

CoffeeScript将其称为存在操作符(具体来说,是存在操作符的“访问变量”):

存在操作符的访问变量?可用于吸收属性链中的空引用。使用它代替点访问器。在基值可能为空或未定义的情况下。

c#将其称为空条件操作符。

空条件操作符应用成员访问?。,或元素访问,?[],仅当该操作数的计算结果为非空时才对其操作数进行操作;否则,返回null。

Kotlin将其称为安全调用运算符。

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

其他回答

是的。从TypeScript 3.7(2019年11月5日发布)开始,这个特性就被支持了,被称为可选链接:

在其核心,可选的链接让我们编写的代码中,TypeScript可以立即停止运行一些表达式,如果我们遇到一个null或未定义。可选连锁的明星是新的?操作符用于可选属性访问。

更多细节请参考TypeScript 3.7发布说明。


在3.7版本之前,TypeScript中不支持这一点,尽管早在TypeScript回购的第16个问题(可以追溯到2014年)中就已经请求过了。

至于如何称呼这个运算符,似乎还没有达成共识。除了“可选链接”(这也是它在JavaScript和Swift中被称为的),还有其他一些例子:

CoffeeScript将其称为存在操作符(具体来说,是存在操作符的“访问变量”):

存在操作符的访问变量?可用于吸收属性链中的空引用。使用它代替点访问器。在基值可能为空或未定义的情况下。

c#将其称为空条件操作符。

空条件操作符应用成员访问?。,或元素访问,?[],仅当该操作数的计算结果为非空时才对其操作数进行操作;否则,返回null。

Kotlin将其称为安全调用运算符。

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

我觉得这就是你要找的东西。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);

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

const result = a?.b?.c;

从长期和短期来看,TypeScript团队正在等待ECMAScript规范的收紧,这样他们的实现就可以在未来不中断。如果他们现在实现了一些东西,如果ECMAScript重新定义了他们的规范,那么最终需要进行重大更改。

参见可选连接规范

如果有些东西永远不会成为标准JavaScript, TypeScript团队可以按照他们认为合适的方式实现,但对于未来的ECMAScript添加,他们希望保留语义,即使他们提供早期访问,就像他们对许多其他特性一样。

捷径

所以JavaScripts所有时髦的操作符都是可用的,包括类型转换,比如…

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

手动解决方案

回到问题上来。我有一个愚蠢的例子,说明如何在JavaScript(以及TypeScript)中做类似的事情,尽管我绝对不是说它是你真正追求的一个优雅的特性。

(foo||{}).bar;

如果foo未定义,结果也未定义如果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'));

操作员吗?。在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中的智能感知一起工作得非常好。

正如之前所回答的,目前它仍在考虑中,但到目前为止它已经死在水里几年了。

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

斯菲德尔

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'));