类型断言

类型断言

TypeScript 允许你覆盖它的推断,并且能以你任何你想要的方式分析它,这种机制被称为类型断言。TypeScript 类型断言用来告诉编译器你比它更了解这个类型,并且它不应该再发出错误。类型断言的一个常见用例是当你从 JavaScript 迁移到 TypeScript 时:

const foo = {};
foo.bar = 123; // Error: 'bar' 属性不存在于 ‘{}’
foo.bas = "hello"; // Error: 'bas' 属性不存在于 '{}'

这里的代码发出了错误警告,因为 foo 的类型推断为 {},即是具有零属性的对象。因此,你不能在它的属性上添加 bar 或 bas,你可以通过类型断言来避免此问题:

interface Foo {
  bar: number;
  bas: string;
}

const foo = {} as Foo;
foo.bar = 123;
foo.bas = "hello";

TypeScript 会在变量属性访问时进行强制空检测,这就促成了大量的前置检测代码,其在提高整体代码安全性的同时,对配置文件这样的静态数据就会造成冗余:

const config = {
  port: 8000,
};

if (config) {
  console.log(config.port);
}

TypeScript 2.0 中提供了非空断言标志符:

console.log(config!.port);

类型断言,尽管我们已经证明了它并不是那么安全,但它也还是有用武之地。如下一个非常实用的例子所示,当使用者了解传入参数更具体的类型时,类型断言能按预期工作:

function handler(event: Event) {
  const mouseEvent = event as MouseEvent;
}

然而,如下例子中的代码将会报错,尽管使用者已经使用了类型断言:

function handler(event: Event) {
  const element = event as HTMLElement; // Error: 'Event' 和 'HTMLElement' 中的任何一个都不能赋值给另外一个
}

如果你仍然想使用那个类型,你可以使用双重断言。首先断言成兼容所有类型的 any,编译器将不会报错:

function handler(event: Event) {
  const element = event as any as HTMLElement; // ok
}

类型捕获

TypeScript 类型系统非常强大,它支持其他任何单一语言无法实现的类型流动和类型片段。

复制类型和值

如果你想移动一个类,你可能会想要做以下事情:

class Foo {}

const Bar = Foo;

let bar: Bar; // Error: 不能找到名称 'Bar'

这会得到一个错误,因为 const 仅仅是复制了 Foo 到一个变量声明空间,因此你无法把 Bar 当作一个类型声明使用。正确的方式是使用 import 关键字,请注意,如果你在使用 namespace 或者 modules,使用 import 是你唯一能用的方式:

namespace importing {
  export class Foo {}
}

import Bar = importing.Foo;
let bar: Bar; // ok

这个 import 技巧,仅适合于类型和变量。

捕获变量的类型

你可以通过 typeof 操作符在类型注解中使用变量。这允许你告诉编译器,一个变量的类型与其他类型相同,如下所示:

let foo = 123;
let bar: typeof foo; // 'bar' 类型与 'foo' 类型相同(在这里是:'number')

bar = 456; // ok
bar = "789"; // Error: 'string' 不能分配给 'number' 类型

捕获类成员的类型

与捕获变量的类型相似,你仅仅是需要声明一个变量用来捕获到的类型:

class Foo {
  foo: number; // 我们想要捕获的类型
}

declare let _foo: Foo;

// 与之前做法相同
let bar: typeof _foo.foo;

捕获字符串类型

许多 JavaScript 库和框架都使用原始的 JavaScript 字符串,你可以使用 const 定义一个变量捕获它的类型:

// 捕获字符串的类型与值
const foo = "Hello World";

// 使用一个捕获的类型
let bar: typeof foo;

// bar 仅能被赋值 'Hello World'
bar = "Hello World"; // ok
bar = "anything else"; // Error

在这个例子里,bar 有字面量类型 Hello World,我们在字面量类型章节已经深入讨论。

捕获键的名称

keyof 操作符能让你捕获一个类型的键。例如,你可以使用它来捕获变量的键名称,在通过使用 typeof 来获取类型之后:

const colors = {
  red: "red",
  blue: "blue",
};

type Colors = keyof typeof colors;

let color: Colors; // color 的类型是 'red' | 'blue'
color = "red"; // ok
color = "blue"; // ok
color = "anythingElse"; // Error

这允许你很容易地拥有像字符串枚举+常量这样的类型,如上例所示。

下一页