ChatGPT解决这个技术问题 Extra ChatGPT

Typescript 是否支持 ?.操作员? (而且,它叫什么?)

Typescript 当前(或是否有计划)支持 ?.safe navigation 运算符

IE:

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

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

@mattytommo,您在 c# 中确实有它,它称为空合并运算符并使用 ??语法 weblogs.asp.net/scottgu/archive/2007/09/20/…
@BasaratAli 不幸的是,coalesce 对 property ?? property2 很好,但如果您尝试 property.company ?? property1.company 并且 property 为空,您会得到一个 NullReferenceException
@mattytommo 这对于 C# 现在确实存在:msdn.microsoft.com/en-us/library/dn986595.aspx
拜访我们的微软代表称它为猫王接线员,因为问号看起来像猫王的头发和他正在唱歌的麦克风......
它是在 v3.7 中添加的,称为 Optional Chaining。有关代码示例,请参阅我的答案。

D
Donut

是的。 从 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 JavaScriptSwift)之外,还有一些其他示例:

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

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

C# 将此称为空条件运算符。

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

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

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


“存在运算符的访问器变体”。自然。如此吸引人,几乎不可能忘记。 :)。感谢您非常彻底的回答。
@MartyPitt 当然可以!我同意,我很乐意看到 a) 更广泛地采用这样的运算符(请使用 C#!)和 b) 一个更好的名称(您链接的博客文章中的“安全导航”运算符有一个不错的选择)。
Angular 在其模板中实现了这一点:angular.io/guide/…
在其他一些语言中,它被称为“猫王”运算符
它是针对 TypeScript 3.7.0 (github.com/microsoft/TypeScript/issues/…) 发布的
A
A. K-R

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

旧答案:关于布尔运算符的标准 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";

只要陈述为真,&& 就会进行评估。如果为真,则返回最后一个值。如果为假,则返回第一个评估为假的值。这可能是 0、null、false 等。||返回第一个计算结果为 true 的值。
如果 bar 已定义但计算结果为 false(如布尔值 false 或零),则效果不佳。
F
Fenton

这是在 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;

因此,如果 fooundefined,则结果为 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 || {}).nativeElementProperty 'nativeElement' does not exist on type '{}'. any this.loop typeof angular.io/api/core/ElementRef
@Kuncevic - 您需要... 1)提供兼容的默认值来代替 {},或 2)使用类型断言来使编译器静音。
假设 foo 是一个实际有用的对象:(foo || {}).bar 通常不会在 typescript 中编译,因为 {}foo 的类型不同。这就是@VeganHunter 的解决方案旨在避免的问题。
@Simon_Weaver 然后 (foo || {bar}).bar 将使编译器顺利运行,我认为冗长是可以接受的。
@harps 实际上,这仅在 bar 被定义为变量时才编译,它很可能不会是
b
basarat

更新:是的,现在支持!

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


J
Jose A

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
}

? 不同,因为它断言该值已定义。 ? 预计会静默失败/评估为假。无论如何,很高兴知道。
现在我想起来了......这个答案毫无意义,因为它没有执行 C# 运算符所做的“安全导航”。
不过,这回答了我的问题。我知道吗?来自 c# 并在打字稿中尝试过。它没有用,但我看到了!存在但不知道它做了什么。我想知道它是否相同,进行了谷歌搜索,并找到了解决这个问题的方法,这告诉我不,它们是不同的。
s
superluminary

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

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

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

属性访问也是可能的:cats?.['miows']

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


请纠正我,但 Elvis 运算符至少在 Kotlin ?: 中。你有参考吗?
很快就会在纯 JS 中得到支持 - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
TypeScript 3.7 版本的公告提到它:devblogs.microsoft.com/typescript/announcing-typescript-3-7
z
zoran404

终于来了!

这里有一些例子:

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


V
VeganHunter

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


这正是我想要的!它适用于打字稿 2.1.6。
或者你可以称之为 elvis<T> ;-)
Simon_Weaver,我称它为“悲伤的小丑” :o(
p
phidias

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

/** * 对象深度属性的类型安全访问 * * @param obj 获取深度属性的对象 * @param unsafeDataOperation 返回深度属性的函数 * @param valueIfFail 如果没有此类属性,则返回值 * / 导出函数 getInSafe(obj: O, unsafeDataOperation: (x: O) => T, valueIfFail?: any) : T { try { return unsafeDataOperation(obj) } catch (error) { return valueIfFail; } } //示例用法:getInSafe(sellTicket, x => x.phoneDetails.imeiNumber, ''); //上面的例子 getInSafe(foo, x => x.bar.check, null);


凉爽的!!有什么注意事项吗?我有一个包含大约 20 个 getter 的包装类,每个都有以下类型的返回 - 并且所有字段都必须为空检查 return this.entry.fields.featuredImage.fields.file.url;
唯一需要注意的可能是性能影响,但我没有资格谈论各种 JIT 人员如何处理这个问题。
S
Simon_Weaver

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

 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;

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


D
Damian Green

它称为可选链,位于 Typescript 3.7

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


B
Benny Bottema

如前所述,目前仍在考虑中,但has been dead in the water到现在已经有几年了。

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

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

J
Jamon Holmgren

还没有(截至 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 个月左右会过时,但希望它同时对某人有所帮助。


J
Josef

我想这就是你要找的。 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);

A
Angelos Pikoulas

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