# 断言
# 类型断言
类型断言好比其他语言里面的类型转换,但是不进行特殊的数据检查和解构。它没有运行时的影响,只是在编译阶段起作用。TypeScript会假设你,程序员,已经进行了必须的检查。
- 尖括号语法
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;
# 非空断言
在上下文中,当类型检查器无法断定类型时,后缀表达式操作符 !
可以用于断言操作对象的非空性。!
将从操作对象类型里去除 null
和 undefined
:
- 忽略 undefined 和 null
function myFunc(maybeString: string | null | undefined) {
// 'string | null | undefined' 类型不能赋值给 'string' 类型。
const onlyString: string = maybeString; // Error
const ingoreUndefinedAndNull: string = maybeString!; // Ok
}
- 调用函数时忽略 undefined 类型
type NumGenerator = () => number;
function myFunc(numGenerator: NumGenerator | undefined) {
// NumGenerator 可能为 undefined, 所以不能调用
const num1 = numGenerator(); // Error
const num2 = numGenerator!(); // Ok
}
# 确定赋值断言
允许在实例属性和变量声明后面放置一个 !
号,从而告诉 TypeScript 该属性会被明确地赋值。
let x: number;
initialize();
// 变量 x 在赋值前被使用
console.log(2 * x); // Error
function initialize() {
x = 10;
}
使用确定赋值断言,可以告诉 TypeScript 该属性会被明确地赋值。
代码语言:javascript复制let x!: number;
initialize();
console.log(2 * x); // Ok
function initialize() {
x = 10;
}
# 类型守卫
类型保护时可执行运行时检查的一种表达式,可以确保该类型在一定的范围内。或者说,类型保护可以保证一个字符串是一个字符串,尽管它的值可以是一个数值。
类型保护与特性检测并不是完全不同,其主要思想是尝试检测属性、方法或原型,以确定如何处理值。
# in
代码语言:javascript复制interface Admin {
name: string;
privileges: string[];
}
interface Employee {
name: string;
startDate: Date;
}
type UnkonwnEmployee = Admin | Employee;
function printEmployeeInformation(emp: UnkonwnEmployee) {
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
代码语言:javascript复制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}'.`);
}
typeof
类型保护只支持两种形式:typeof v === "typename"
和 typeof v !== "typename"
,"typename"
必须是 "number"
,"string"
,"boolean"
或 "symbol"
。
# instanceof
代码语言:javascript复制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) {
// 类型收窄为 'SpaceRepeatingPadder'
}
# 自定义类型保护
代码语言:javascript复制function isNumber(x: any): x is number {
return typeof x === "number";
}
function isString(x: any): x is string {
return typeof x === "string";
}
# 联合类型和类型别名
# 联合类型
联合类型通常与 null
或 undefined
一起使用
const sayHello = (name: string | undefined) => {
/* ... */
};
sayHello("Cell");
sayHello(undefined);
类型 A 和 类型 B 联合后的类型是同时接受 A 和 B 的类型。
- 字面量类型
// 字面量类型,用约束取值只能是某几个值中的一个
let num: 1 | 2 = 1;
type EventNames = "click" | "scroll" | "mousemove";
# 可辨识联合
也称为代数数据类型或标签联合类型。包含 3 个要点:可辨识、联合类型、类型守卫。
本质是结合联合类型和字面量类型的一种类型保护方法。如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块。
- 可辨识
// 可辨识要求联合类型中的每个元素都含有一个单例属性
enum CarTransmission {
Automatic = 200,
Manual = 300
}
interface Motorcycle {
vType: "motorcycle"; // 可辨识
make: number;
}
interface Car {
vType: "car"; // 可辨识
transmission: CarTransmission;
}
interface Truck {
vType: "truck"; // 可辨识
payloadPounds: number;
}
Motorcycle
、Car
、Truck
都有一个 vType
属性,它们的值分别是 "motorcycle"
、"car"
、"truck"
,这就是可辨识属性,其他的属性只跟特性的接口有关。
- 联合类型
type Vehicle = Motorcycle | Car | Truck;
- 类型守卫
const EVALUATION_FACTOR = Math.PI;
function evaluatePrice(vehicle: Vehicle) {
return vehicle.payloadPounds * EVALUATION_FACTOR;
}
const myTruck: Truck = {
vType: "truck",
payloadPounds: 10000
};
evaluatePrice(myTruck); //
以上内容会报错
代码语言:javascript复制// Property 'payloadPounds' does not exist on type 'Vehicle'.
// Property 'payloadPounds' does not exist on type 'Motorcycle'.
因为在 Motorcycle
,Car
,Truck
中都有 vType
属性,但是 Motorcycle
中没有 payloadPounds
属性,所以 evaluatePrice
函数中的 vehicle.payloadPounds
会报错。
function evaluatePrice(vehicle: Vehicle) {
switch (vehicle.vType) {
case "car":
return vehicle.transmission * EVALUATION_FACTOR;
case "motorcycle":
return vehicle.make * EVALUATION_FACTOR;
case "truck":
return vehicle.payloadPounds * EVALUATION_FACTOR;
}
}
使用 switch
语句来进行类型守卫,从而确保在 evaluatePrice
方法中,可以安全访问 vehicle
所包含的属性。
# 类型别名
类型别名用来给一个类型起个新名字。
代码语言:javascript复制type Message = string | string[];
let greet = (message: Message) => {
/* ... */
};
# 交叉类型
在 TypeScript 中交叉类型是将多个类型合并为一个类型,通过 &
运算符可以将现有的多种类型叠加到一起成为一种类型,这种类型拥有所有类型的特性。
type PartialPointX = { x: number };
type Point = PartialPointX & { y: number };
let point: Point = { x: 1, y: 2 };
# 同名基础类型属性的合并
代码语言:javascript复制interface X {
c: string;
d: string;
}
interface Y {
c: number;
e: string;
}
type XY = X & Y;
type YX = Y & X;
X
和 Y
都含有一个相同的成员 c
,但是它们的类型不一致。对于这种情况,此时 XY
类型或 YX
类型中成员 c
的类型 string & number
,这种类型是不存在的,所以此时成员 c
的类型是 never
。
# 同名非基础类型属性的合并
代码语言:javascript复制interface D {
d: boolean;
}
interface E {
e: string;
}
interface F {
f: number;
}
interface A { x: D; }
interface B { x: E; }
interface C { x: F; }
type ABC = A & B & C;
let abc: ABC = {
x: {
d: true,
e: "Cell",
f: 2022
}
};
console.log('abc:', abc);
// "use strict";
// let abc = {
// x: {
// d: true,
// e: "Cell",
// f: 2022
// }
// };
// console.log('abc:', abc);
在混入多个类型时,若存在相同的成员,且成员类型为非基本数据类型,那么是可以成功合并。