在 Angular 2 模板中,安全运算符 ?.
有效,但在使用 TypeScript 2.0 的 component.ts
中无效。此外,安全导航运算符 (!.
) 不起作用。
例如:
这个打字稿
if (a!.b!.c) { }
编译为此 JavaScript
if (a.b.c) { }
但是当我运行它时,我收到以下错误:
无法读取未定义的属性“b”
是否有以下替代方案?
if (a && a.b && a.b.c) { }
!
是 non-null assertion operator(后缀表达式)- 它只是告诉类型检查器您确定 a
不是 null
或 undefined
。
操作了!生成 a 类型的值,其中排除 null 和未定义
Optional chaining 最后made 将其写入打字稿 (3.7) 🎉
可选的链接运算符 ?。允许读取位于连接对象链深处的属性值,而无需明确验证链中的每个引用是否有效。这 ?。运算符的功能与 .链接运算符,除了如果引用为空(null 或未定义)时不会导致错误,表达式会短路并返回未定义的值。与函数调用一起使用时,如果给定函数不存在,则返回 undefined。
句法:
obj?.prop // Accessing object's property
obj?.[expr] // Optional chaining with expressions
arr?.[index] // Array item access with optional chaining
func?.(args) // Optional chaining with function calls
注意:
可选链接在分配的左侧无效
const object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment
自 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
或 undefined
时才会调用 otherFn
在 IF 语句中使用可选链
这个:
if (someObj && someObj.someProperty) {
// ...
}
现在可以用这个替换
if (someObj?.someProperty) {
// ...
}
参考:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html
!
而不是 ?
则它可以工作
?.
) 是在 3.7 中引入的,并且您正在运行 3.5.3。在 3.7 之前的版本中,TypeScript 假定 ?
以简写 if 形式 (foo ? bar : baz
) 使用,因此期望表达式的其余部分
更新:
计划在 3.7 版本范围内
https://github.com/microsoft/TypeScript/issues/33352
您可以尝试编写这样的自定义函数。
该方法的主要优点是类型检查和部分智能感知。
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;
}
及用法(最多支持5个参数,可扩展):
nullSafe(a, 'b', 'c');
playground 上的示例。
另一种使用外部库的替代方法是来自 Lodash 的 _.has()。
例如
_.has(a, 'b.c')
等于
(a && a.b && a.b.c)
编辑:如评论中所述,使用此方法时您会丢失 Typescript 的类型推断。例如,假设一个对象的类型正确,如果 z 未定义为对象 b 的字段,则使用 (a && ab && abz) 会出现编译错误。但是使用 _.has(a, 'bz'),不会出现该错误。
一个名为 ts-optchain 的新库提供了此功能,并且与 lodash 的解决方案不同,它还可以确保您的类型安全,这里是它的使用示例(摘自自述文件):
import { oc } from 'ts-optchain';
interface I {
a?: string;
b?: {
d?: string;
};
c?: Array<{
u?: {
v?: number;
};
}>;
e?: {
f?: string;
g?: () => string;
};
}
const x: I = {
a: 'hello',
b: {
d: 'world',
},
c: [{ u: { v: -100 } }, { u: { v: 200 } }, {}, { u: { v: -300 } }],
};
// Here are a few examples of deep object traversal using (a) optional chaining vs
// (b) logic expressions. Each of the following pairs are equivalent in
// result. Note how the benefits of optional chaining accrue with
// the depth and complexity of the traversal.
oc(x).a(); // 'hello'
x.a;
oc(x).b.d(); // 'world'
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(); // undefined
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(); // undefined
x.e && x.e.f;
oc(x).e.f('optional default value'); // 'optional default value'
(x.e && x.e.f) || 'optional default value';
// NOTE: working with function value types can be risky. Additional run-time
// checks to verify that object types are functions before invocation are advised!
oc(x).e.g(() => 'Yo Yo')(); // 'Yo Yo'
((x.e && x.e.g) || (() => 'Yo Yo'))();
基于@Pvl's answer,如果您使用覆盖,您也可以在返回值中包含类型安全:
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;
}
playground 上的示例。
typescript 3.7 以上的版本支持 typescript < 3.7 的安全导航运算符我制作了这个很有用的功能。
export function isAccessible(data, keys, start=0) {
if (start == 0 && (data == null || data == undefined)) {
console.warn("data",data);
return false;
} else {
if (data[keys[start]] == null || data[keys[start]] == undefined) {
console.warn("Object valid till", keys.slice(0,start),keys[start],"undefined");
return false;
} else {
if (start + 1 >= keys.length) {
return data[keys[start]];
}
return this.isAccessible(data[keys[start]], keys, start + 1);
}
}
}
代码中的函数调用
假设我们有一个对象 obj
的键可以变化,并且我们想检查 obj.key1.key2
是否可以访问,那么函数调用将如下所示:
isAccessible(Object,["key1","key2"])