TypeScript“奇怪”的符号

本文将分享这些年在学习 TypeScript 过程中,遇到的 10 大 “奇怪” 的符号。其中有一些符号,码云笔记第一次见的时候也觉得 “一脸懵逼”,希望本文对学习 TypeScript 的小伙伴能有一些帮助。

! 非空断言操作符

在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。具体而言,x! 将从 x 值域中排除 null 和 undefined 。

function myFunc(maybeString: string | undefined | null) {
  const onlyString: string = maybeString; // Error
  const ignoreUndefinedAndNull: string = maybeString!; // Ok
}

调用函数时忽略 undefined 类型 场景

type NumGenerator = () => number;
function myFunc(numGenerator: NumGenerator | undefined) {
  const num1 = numGenerator(); // Error
  const num2 = numGenerator!(); //OK
}

?. 运算符

TypeScript 3.7 实现了呼声最高的 ECMAScript 功能之一:可选链(Optional Chaining)。有了可选链后,我们编写代码时如果遇到 null 或 undefined 就可以立即停止某些表达式的运行。可选链的核心是新的 ?. 运算符

obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
const val = a?.b;

语句编译生成的 ES5 代码:

var val = a === null || a === void 0 ? void 0 : a.b;

可选链与函数调用

let result = obj.customMethod?.();

?? 空值合并运算符

在 TypeScript 3.7 版本中除了引入了前面介绍的可选链 ?. 之外,也引入了一个新的逻辑运算符 —— 空值合并运算符 ??
当左侧操作数为 null 或 undefined 时,其返回右侧的操作数,否则返回左侧的操作数。

const foo = null ?? 'default string';
console.log(foo); // 输出:"default string"

短路

不能与 && 或 || 操作符共用

(null || undefined ) ?? "foo"; // 返回 "foo"

与可选链操作符 ?. 的关系

空值合并运算符针对 undefined 与 null 这两个值,可选链式操作符 ?. 也是如此。可选链式操作符,对于访问属性可能为 undefined 与 null 的对象时非常有用。

interface Customer {
  name: string;
  city?: string;
}

let customer: Customer = {
  name: "Semlinker"
};

let customerCity = customer?.city ?? "Unknown city";
console.log(customerCity); // 输出:Unknown city

?: 可选属性

在面向对象语言中,接口是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类去实现。 TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

& 运算符

在 TypeScript 中交叉类型是将多个类型合并为一个类型。通过 & 运算符可以将现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

let point: Point = {
  x: 1,
  y: 1
}

| 分隔符

在 TypeScript 中联合类型(Union Types)表示取值可以为多种类型中的一种,联合类型使用 | 分隔每个类型。联合类型通常与 null 或 undefined 一起使用:

const sayHello = (name: string | undefined) => { /* ... */ };

类型保护 in 关键字

interface Admin {
  name: string;
  privileges: string[];
}

interface Employee {
  name: string;
  startDate: Date;
}

type UnknownEmployee = Employee | Admin;

function printEmployeeInformation(emp: UnknownEmployee) {
  console.log("Name: " + emp.name);
  if ("privileges" in emp) {
    console.log("Privileges: " + emp.privileges);
  }
  if ("startDate" in emp) {
    console.log("Start Date: " + emp.startDate);
  }
}

typeof 关键字

function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
      return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
      return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

instanceof 关键字

interface Padder {
  getPaddingString(): string;
}

class SpaceRepeatingPadder implements Padder {
  constructor(private numSpaces: number) {}
  getPaddingString() {
    return Array(this.numSpaces + 1).join(" ");
  }
}

class StringPadder implements Padder {
  constructor(private value: string) {}
  getPaddingString() {
    return this.value;
  }
}

let padder: Padder = new SpaceRepeatingPadder(6);

if (padder instanceof SpaceRepeatingPadder) {
  // padder的类型收窄为 'SpaceRepeatingPadder'
}

TypeScript 断言 <Type>

通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。类型断言好比其他语言里的类型转换,但是不进行特殊的数据检查和解构。它没有运行时的影响,只是在编译阶段起作用。

“尖括号” 语法

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

as 语法

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

TypeScript 泛型

泛型详解
参考上面的图片,当我们调用 identity(1) ,Number 类型就像参数 1 一样,它将在出现 T 的任何位置填充该类型。图中 内部的 T 被称为类型变量,它是我们希望传递给 identity 函数的类型占位符,同时它被分配给 value 参数用来代替它的类型:此时 T 充当的是类型,而不是特定的 Number 类型。

其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:

K(Key):表示对象中的键类型;
V(Value):表示对象中的值类型;
E(Element):表示元素类型。
其实并不是只能定义一个类型变量,我们可以引入希望定义的任何数量的类型变量。比如我们引入一个新的类型变量 U,用于扩展我们定义的 identity 函数:

function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity<Number, string>(68, "Semlinker"));

@XXX 装饰器 装饰器语法

@Plugin({
  pluginName: 'Device',
  plugin: 'cordova-plugin-device',
  pluginRef: 'device',
  repo: 'https://github.com/apache/cordova-plugin-device',
  platforms: ['Android', 'Browser', 'iOS', 'macOS', 'Windows'],
})
@Injectable()
export class Device extends IonicNativePlugin {}

类装饰器声明:

declare type ClassDecorator = <TFunction extends Function>(
  target: TFunction
) => TFunction | void;

属性装饰器

declare type PropertyDecorator = (target:Object, 
propertyKey: string | symbol ) => void;

案例

function logProperty(target: any, key: string) {
  delete target[key];

  const backingField = "_" + key;

  Object.defineProperty(target, backingField, {
    writable: true,
    enumerable: true,
    configurable: true
  });

  // property getter
  const getter = function (this: any) {
    const currVal = this[backingField];
    console.log(`Get: ${key} => ${currVal}`);
    return currVal;
  };

  // property setter
  const setter = function (this: any, newVal: any) {
    console.log(`Set: ${key} => ${newVal}`);
    this[backingField] = newVal;
  };

  // Create new property with getter and setter
  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}

class Person { 
  @logProperty
  public name: string;

  constructor(name : string) { 
    this.name = name;
  }
}

const p1 = new Person("semlinker");
p1.name = "kakuqo";

方法装饰器

function LogOutput(tarage: Function, key: string, descriptor: any) {
  let originalMethod = descriptor.value;
  let newMethod = function(...args: any[]): any {
    let result: any = originalMethod.apply(this, args);
    if(!this.loggedOutput) {
      this.loggedOutput = new Array<any>();
    }
    this.loggedOutput.push({
      method: key,
      parameters: args,
      output: result,
      timestamp: new Date()
    });
    return result;
  };
  descriptor.value = newMethod;
}

class Calculator {
  @LogOutput
  double (num: number): number {
    return num * 2;
  }
}

let calc = new Calculator();
calc.double(11);
// console ouput: [{method: "double", output: 22, ...}]
console.log(calc.loggedOutput);

参 数装饰器

function Log(target: Function, key: string, parameterIndex: number) {
  let functionLogged = key || target.prototype.constructor.name;
  console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has
    been decorated`);
}

class Greeter {
  greeting: string;
  constructor(@Log phrase: string) {
    this.greeting = phrase; 
  }
}

原文地址:https://www.cnblogs.com/boyGdm/p/14488928.html