Typescript 安全导航运算符(?)或(!)和空属性路径

Typescript 安全导航运算符(?)或(!)和空属性路径,typescript,Typescript,在Angular 2模板中,安全运算符?。起作用,但在组件.ts中不能使用TypeScript 2.0。此外,安全导航操作员(!。)也不工作 例如: 这是打字稿 if (a!.b!.c) { } 编译到这个JavaScript if (a.b.c) { } 但当我运行它时,我得到以下错误: 无法读取未定义的属性“b” 除了以下内容,还有其他选择吗 if (a && a.b && a.b.c) { } is(后期修复表达式)-它只是告诉类型检查器您确认a不是n

在Angular 2模板中,安全运算符
?。
起作用,但在
组件.ts中不能使用TypeScript 2.0。此外,安全导航操作员(
!。
)也不工作

例如:

这是打字稿

if (a!.b!.c) { }
编译到这个JavaScript

if (a.b.c) { }
但当我运行它时,我得到以下错误:

无法读取未定义的属性“b”

除了以下内容,还有其他选择吗

if (a && a.b && a.b.c) { }
is(后期修复表达式)-它只是告诉类型检查器您确认
a
不是
null
未定义的

操作
a生成类型为
a
的值,其中
null
undefined
被排除在外



最后,将其转换为typescript(3.7)另一种使用外部库的替代方法是from

例如

等于

(a && a.b && a.b.c)
编辑: 如注释中所述,在使用此方法时,您失去了Typescript的类型推断。
例如,假设一个人的对象类型正确,如果z未定义为对象b的字段,则会出现编译错误(a&&a.b&&a.b.z)。但是使用has(a,'b.z')时,不会出现该错误。

更新:

计划在3.7版本的范围内


您可以尝试编写这样的自定义函数

该方法的主要优点是类型检查和部分智能感知

export function nullSafe<T, 
    K0 extends keyof T, 
    K1 extends keyof T[K0],
    K2 extends keyof T[K0][K1],
    K3 extends keyof T[K0][K1][K2],
    K4 extends keyof T[K0][K1][K2][K3],
    K5 extends keyof T[K0][K1][K2][K3][K4]>
    (obj: T, k0: K0, k1?: K1, k2?: K2, k3?: K3, k4?: K4, k5?: K5) {
    let result: any = obj;

    const keysCount = arguments.length - 1;
    for (var i = 1; i <= keysCount; i++) {
        if (result === null || result === undefined) return result;
        result = result[arguments[i]];
    }

    return result;
}

示例。

基于@Pvl的答案,如果使用覆盖,还可以在返回值中包含类型安全性:

function dig<
  T,
  K1 extends keyof T
  >(obj: T, key1: K1): T[K1];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1]
  >(obj: T, key1: K1, key2: K2): T[K1][K2];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2]
  >(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2],
  K4 extends keyof T[K1][K2][K3]
  >(obj: T, key1: K1, key2: K2, key3: K3, key4: K4): T[K1][K2][K3][K4];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2],
  K4 extends keyof T[K1][K2][K3],
  K5 extends keyof T[K1][K2][K3][K4]
  >(obj: T, key1: K1, key2: K2, key3: K3, key4: K4, key5: K5): T[K1][K2][K3][K4][K5];

function dig<
  T,
  K1 extends keyof T,
  K2 extends keyof T[K1],
  K3 extends keyof T[K1][K2],
  K4 extends keyof T[K1][K2][K3],
  K5 extends keyof T[K1][K2][K3][K4]
  >(obj: T, key1: K1, key2?: K2, key3?: K3, key4?: K4, key5?: K5):
  T[K1] |
  T[K1][K2] |
  T[K1][K2][K3] |
  T[K1][K2][K3][K4] |
  T[K1][K2][K3][K4][K5] {
    let value: any = obj && obj[key1];

    if (key2) {
      value = value && value[key2];
    }

    if (key3) {
      value = value && value[key3];
    }

    if (key4) {
      value = value && value[key4];
    }

    if (key5) {
      value = value && value[key5];
    }

    return value;
}
函数挖掘<
T
K1扩展了keyt
>(obj:T,key1:K1):T[K1];
功能挖掘<
T
K1扩展了T的键,
K2扩展了T的键[K1]
>(obj:T,key1:K1,key2:K2):T[K1][K2];
功能挖掘<
T
K1扩展了T的键,
K2扩展了T[K1]的键,
K3扩展了T[K1][K2]的键
>(obj:T,key1:K1,key2:K2,key3:K3):T[K1][K2][K3];
功能挖掘<
T
K1扩展了T的键,
K2扩展了T[K1]的键,
K3扩展了T[K1][K2]的键,
K4扩展了T[K1][K2][K3]
>(obj:T,key1:K1,key2:K2,key3:K3,key4:K4):T[K1][K2][K3][K4];
功能挖掘<
T
K1扩展了T的键,
K2扩展了T[K1]的键,
K3扩展了T[K1][K2]的键,
K4扩展了T[K1][K2][K3]的键,
K5扩展了T[K1][K2][K3][K4]
>(obj:T,key1:K1,key2:K2,key3:K3,key4:K4,key5:K5):T[K1][K2][K3][K4][K5];
功能挖掘<
T
K1扩展了T的键,
K2扩展了T[K1]的键,
K3扩展了T[K1][K2]的键,
K4扩展了T[K1][K2][K3]的键,
K5扩展了T[K1][K2][K3][K4]
>(对象:T,键1:K1,键2?:K2,键3?:K3,键4?:K4,键5?:K5):
T[K1]|
T[K1][K2]|
T[K1][K2][K3]|
T[K1][K2][K3][K4]|
T[K1][K2][K3][K4][K5]{
let值:any=obj&&obj[key1];
如果(键2){
value=value&&value[key2];
}
如果(键3){
value=value&&value[key3];
}
如果(键4){
value=value&&value[key4];
}
如果(键5){
值=值和值[key5];
}
返回值;
}

示例。

一个名为的新库提供了此功能,与lodash的解决方案不同,它还可以确保您的类型安全,下面是如何使用它的示例(取自自述):

从'ts optchain'导入{oc};
接口I{
a?:字符串;
b:{
d?:字符串;
};
c?:数组;
e?:{
f?:字符串;
g?:()=>字符串;
};
}
常数x:I={
a:你好,
b:{
d:‘世界’,
},
c:[{u:{v:-100},{u:{v:200},{},{u:{v:-300}],
};
//下面是使用(a)可选链接和
//(b)逻辑表达式。以下每一对都是等效的
//结果。注意可选链接的好处是如何随着
//遍历的深度和复杂性。
oc(x).a();/'你好’
x、 a;
oc(x.b.d();/'世界的
x、 b&x.b.d;
oc(x).c[0].u.v();//-100
x、 c&&x.c[0]&&x.c[0]。u&&x.c[0]。u.v;
oc(x).c[100].u.v();//未定义
x、 c&&x.c[100]&&x.c[100]。u&&x.c[100]。u.v;
oc(x).c[100].u.v(1234);//1234
(x.c&&x.c[100]&&x.c[100].u&&x.c[100].u.v)|1234;
oc(x.e.f();//未定义
x、 e&x.e.f;
oc(x).e.f('可选默认值');/'可选默认值'
(x.e&&x.e.f)| |“可选默认值”;
//注意:使用函数值类型可能有风险。附加运行时
//在建议调用之前,检查以验证对象类型是否为函数!
oc(x).例如(()=>“溜溜球”)();/'溜溜球
((x.e&&x.e.g)| |(()=>‘Yo’))();

自从TypeScript 3.7发布以来,您现在可以使用可选链接

属性示例:

let x = foo?.bar.baz();
这等于:

let x = (foo === null || foo === undefined) ?
    undefined :
    foo.bar.baz();
此外,你可以致电:

可选呼叫

function(otherFn: (par: string) => void) {
   otherFn?.("some value");
}
只有当otherFn不等于null或未定义时,才会调用otherFn

使用IF语句中的可选链接

这:

if (someObj && someObj.someProperty) {
    // ...
}
现在可以用这个替换

if (someObj?.someProperty) {
    // ...
}

参考

typescript运算符仅为编译而存在,它们不存在于已编译的javascript中。您发布的错误是运行时错误。o(o(o(o)(测试).level1.level2.level3)或o(o(o(a.b).c).d此解决方案在编译时对我们非常有效,并且具有安全的类型提示,虽然没有其他解决方案可用,但这似乎是一个不错的选择。但是它如何与TypeScript的类型推断一起工作?@VitalyB可能是指导入lodash的相应接口。(导入*as uuu来自“lodash”;)@AnkurArora,我认为VitalyB的意思是,没有使用Lodash方法进行类型检查。例如,假设一个人的对象类型正确,如果z没有定义为对象b的字段,而是使用u.has(a,'b.z'),那么就会出现编译错误(a&&a.b&&a.b.z),你不会得到那个错误。至于import语句,是的,它是绝对需要的。@TiaanM这正是我的意思。但是,我刚刚发现了一个新的库,它涵盖了类型问题:对于那些有兴趣签入sta的人来说
function(otherFn: (par: string) => void) {
   otherFn?.("some value");
}
if (someObj && someObj.someProperty) {
    // ...
}
if (someObj?.someProperty) {
    // ...
}