乐趣区

关于typescript:TypeScript基础之非空断言操作符可选链运算符空值合并运算符

非空断言

先看以下代码:

function handler (arg: string | null | undefined) {
  let str: string = arg;
  // ...  
}

对于以上代码,TypeScript 编译器会提醒以下错误信息:

Type 'string | null | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.

要解决以上问题,咱们能够加个条件判断:

function handler (arg: string | null | undefined) {
  let str: string;
  if (arg) {str = arg;}
  // ...  
}

此外,能够应用 TypeScript2.0 中提供的非空断言操作符 (non-null-assertion-operator)。
语法

x!

非空断言操作符操作符 ! 能够用于断言操作对象是非 null 和非 undefined 类型。即:x! 将从 x 值域中排除 nullundefined

所以以上代码能够革新为:

function handler (arg: string | null | undefined) {
  let str: string = arg!;   // 没故障 
  str.split('');
  // ...  
}

看下编译后果:

"use strict";
function handler(arg) {
  let str = arg;
  str.split('');
    // ...  
}

能够看出 ! 非空断言操作符从编译生成的 JS 代码中移除掉了,如果 handler 函数调用时传入 nullundefined时,会呈现以下运行时谬误:

Uncaught TypeError: Cannot read properties of undefined (reading 'split')

所以在理论应用时须要留神。
留神: 非空断言操作符仅在启用 strictNullChecks 标记的时候才失效。当敞开该标记时,编译器不会查看 undefined 类型和 null 类型的赋值。

可选链运算符

先看以下代码:

type AlbumAPIResponse = {
  title: string;
  artist?: {
    name: string;
    bio?: string;
    previousAlbums?: string[];};
};

const album: AlbumAPIResponse = {title: 'test'};

const maybeArtistBio = album.artist && album.artist.bio || undefined;

const maybeArtistBioElement = album ? album.artist ? album.artist.bio ? album.artist.bio : undefined: undefined :undefined; 

const maybeFirstPreviousAlbum = album.artist && album.artist.previousAlbums && album.artist.previousAlbums[0] || undefined;

为了获取 bio 或者 previousAlbums 堪称是殚精竭虑。
当初能够应用 TypeScript3.7 中提供的可选链 (Optional Chaining), 有了可选链后,咱们编写代码时如果遇到 null 或 undefined 就能够立刻进行某些表达式的运行, 间接返回 undefined . 与函数调用一起应用时,如果给定的函数不存在,则返回 undefined。外围是?. 操作符。

语法

obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)

应用

  1. 可选属性拜访
    那么以上代码就能够精简为:

    const maybeArtistBioElement = album?.["artist"]?.["bio"];
    
    const maybeFirstPreviousAlbum = album?.artist?.previousAlbums?.[0];  

是不是精简很多了。

留神: ?.&& 并不是齐全等价。&& 专门用于检测 falsy 值,比方 空字符串、0、-0、0n、NaN、null、undefined 和 false 等。而 ?. 只会验证对象是否为 nullundefined,对于 0 空字符串 来说,并不会呈现 “短路”

  1. 可选元素拜访
    其行为相似于可选属性拜访,但容许咱们拜访非标识符属性(例如任意字符串、数字和 symbol).
    以下是基于索引拜访数组元素:

    function tryGetFirstElement<T>(arr?: T[]) {return arr?.[0];
    }

    编译后果如下:

    "use strict";
    function tryGetFirstElement(arr) {return arr === null || arr === void 0 ? void 0 : arr[0];
    }

    所以在应用可链接后,就不须要手动编写查看数组是否为 null 或 undefined 的保护性代码了。

  2. 尝试调用一个可能不存在的办法

    async function makeRequest(url: string, log?: (msg: string) => void) {log?.(`Request started at ${new Date().toISOString()}`);
      // 以上代码等价于上面 
      //   if (log != null) {//       log(`Request started at ${new Date().toISOString()}`);
      //   }
      const result = (await fetch(url)).json();
      log?.(`Request finished at ${new Date().toISOString()}`);
      return result;
    }

空值合并运算符

先看以下代码:

interface AppConfiguration {
  name: string;
  items: number;
  active: boolean;
}

function updateApp(config: Partial<AppConfiguration>) {config.name = typeof config.name === "string" ? config.name : "(no name)";
  config.items = typeof config.items === "number" ? config.items : -1;
  config.active = typeof config.active === "boolean" ? config.active : true;
}

留神: Partial<T> 将类型的属性变成可选
那么 Partial<AppConfiguration> 相当于:

interface AppConfiguration {
  name?: string;
  items?: number;
  active?: boolean;
}

以上代码中咱们须要先利用 typeof 判断属性类型是否正确,正确就赋值,否则就是默认值。
咱们不能应用||,因为

''||"(no name)";   // 值是"(no name)"不是""
0 || -1;    // 会返回 -1
false || true;    // 会返回 true

当初能够应用 TypeScript3.7 中提供的空值合并运算符。
当左侧操作数为 null 或 undefined 时,其返回右侧的操作数,否则返回左侧的操作数。。
与逻辑或 || 运算符不同,逻辑或会在左操作数为 falsy 值时返回右侧操作数。也就是说,如果你使⽤ || 来为某些变量设置默认的值时,你可能会遇到意料之外的⾏为。⽐如为 falsy 值(空字符串、0、-0、0n、NaN、null、undefined 和 false)时。
语法

leftExpr ?? rightExpr
应用

  1. 为常量提供默认值,保障常量不为 null 或者 undefined
    以上代码可革新如下:

    function updateApp(config: Partial<AppConfiguration>) {config.name = config.name ?? "(no name)";
      config.items = config.items ?? -1;
      config.active = config.active ?? true;
    }
  2. 短路
    与 OR 和 AND 逻辑操作符类似,当左表达式不为 null 或 undefined 时,不会对右表达式进行求值。

    function A() { console.log('函数 A 被调用了'); return undefined; }
    function B() { console.log('函数 B 被调用了'); return false; }
    function C() { console.log('函数 C 被调用了'); return "foo"; }
    
    console.log(A() ?? C());
    // 顺次打印 "函数 A 被调用了"、"函数 C 被调用了"、"foo"

    编译后果 (编译指标设为 ES2015) 如下:

    "use strict";
    var _a;
    function A() { console.log('函数 A 被调用了'); return undefined; }
    function B() { console.log('函数 B 被调用了'); return false; }
    function C() { console.log('函数 C 被调用了'); return "foo"; }
    console.log((_a = A()) !== null && _a !== void 0 ? _a : C());

    输入后果解释:A() 返回了 undefined,所以操作符两边的表达式都被执行了

留神: 不能与 AND 或 OR 操作符共用
将 ?? 间接与 AND(&&)和 OR(||)操作符组合应用是不可取的(该当是因为空值合并操作符和其余逻辑操作符之间的运算优先级 / 运算程序是未定义的)。

null || undefined ?? "foo";  // '||' and '??' operations cannot be mixed without parentheses.
true || undefined ?? "foo";  // '||' and '??' operations cannot be mixed without parentheses.

然而,如果应用括号来显式表明运算优先级,是没有问题的:

(null || undefined) ?? "foo";

参考资料

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

退出移动版