我们可以使用 &
把两个自定义类型相交得到一个新的类型。例如:
type Admin = {
name: string;
privileges: string[];
};
type Employee = {
name: string;
startDate: Date;
};
type ElevatedEmployee = Admin & Employee;
const e1: ElevatedEmployee = {
name: "Lebron",
privileges: ["create-server"],
startDate: new Date()
};
两个类型相交后,得到的是
同样地,也适用于多个 Interfaces 的相交。
如果想判断一个变量是否属于基本数据类型,可以使用 typeof
。例如:
let a: any;
a = "I'm a string";
if (typeof a === "string") {
console.log(a);
}
如果想访问自定义类型变量的属性或者函数,如何判断?这里介绍两种方法。
我们先来看下面例子:
type UnknownEmployee = Employee | Admin;
function printEmployeeInfo(emp: UnknownEmployee) {
console.log(`Name: ${emp.name}`);
if ("privileges" in emp) {
console.log(`Privileges: ${emp.privileges}`);
}
if ("startDate" in emp) {
console.log(`Privileges: ${emp.startDate}`);
}
}
1)首先定义了一个 UnknownEmployee
,它是 Employee
和 Admin
的联合类型。
2)在 printEmployeeInfo
方法实现中,因为 Employee
和 Admin
都有定义 name
属性,所以可以直接访问 emp.name
。
3)privileges
和 startDate
不是 Employee
和 Admin
都有的属性,所以需要进行判断才能访问对应的属性。这里可以使用类似 "privileges" in emp
去判断一个变量中是否有这个属性。
我们先来看下面例子:
interface Bird {
type: "bird";
flyingSpeed: Number;
}
interface Horse {
type: "horse";
runningSpeed: Number;
}
type Animal = Bird | Horse;
function moveAnimal(animal: Animal) {
let speed;
switch (animal.type) {
case "bird":
speed = animal.flyingSpeed;
break;
case "horse":
speed = animal.runningSpeed;
break;
}
console.log(`Moving at speed: ${speed}`);
}
例子中,为了区别 animal
是 Bird
还是 Horse
,在接口定义时添加了字面类型 type
。这样就可以在访问 animal
的属性时,使用 type
去区分它们。
如果想判断一个变量是否是某个类的实例,可以使用 instanceof
。例如:
class Car {
drive() {
console.log("Driving a car ...");
}
}
class Truck {
drive() {
console.log("Driving a truck ...");
}
loadCargo(amount: number) {
console.log(`Loading cargo ...${amount}`);
}
}
type Vehicle = Car | Truck;
function userVehicle(vehicle: Vehicle) {
vehicle.drive();
if (vehicle instanceof Truck) {
vehicle.loadCargo(1000);
}
}
另外,还可以使用上面的 in
: "loadCargo" in vehicle
。
使用 as
把一个变量转换成更具体的类型。例如,假设有一个 input
标签:
const userInputElement = document.getElementById(
"user-input"
) as HTMLInputElement;
userInputElement.value = "Hi, there!";
如果不使用 as
转换成 HTMLInputElement
,访问 value
属性时会报错,因为 getElementById
返回的是 HTMLElement
类型,没有 value
属性。
假设要对用户信息进行验证,然后给用户一些错误提示。例如我们要验证用户的 email
和 username
,有可能会出现以下错误:
{
email: "Not valid email",
username: "Can't be empty"
}
但是这两个错误不一定都存在,可能只有其中的一个,也可能没有。
对于这种情况,我们想写一个 Interface 来表达这些错误。也许我们可以这样写:
interface ErrorContainer {
email: string;
username: string;
}
这样的话,我们声明错误的时候,如果只有一个错误,那么其中一个属性就必须设置为 null
,不能省略。如果有更多不同的错误,这种方法就不够灵活。
下面我们来看一下如何用索引属性解决这个问题。
因为 Error 的具体字段暂时不知道,但是我们知道字段类型是 string
,字段对应的内容也是 string
。所以可以把接口定义为:
interface ErrorContainer {
[prop: string]: string;
}
[prop: string]: string;
中,第一个 string
代表字段的类型,第二个 string
代表值的类型。
我们的 ErrorContainer 就变得很灵活:
const error1: ErrorContainer = {};
const error2: ErrorContainer = {
email: "Not valid email"
};
const error3: ErrorContainer = {
email: "Not valid email",
username: "Can't be empty"
};
假设有以下代码:
type Combinable = string | number;
function add(a: Combinable, b: Combinable) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
const combined = add("a", "b");
从上面的代码可以知道,add("a", "b")
的返回值本质上应该是一个 string
,但是 TypeScript 只知道它是一个 Combinable
类型。
我们可以通过方法重载,让 add("a", "b")
直接返回个 string
类型:
function add(a: string, b: string): string;
function add(a: number, b: number): number;
function add(a: number, b: string): string;
function add(a: string, b: number): string;
function add(a: Combinable, b: Combinable) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
在 add(a: Combinable, b: Combinable)
上面添加所有可能的参数组合,就可以在调用的时候直接返回对应的返回值。
假设有以下数据:
const fetchedUserData = {
id: "1",
name: "Lebron",
job: { title: "CEO", description: "My own company" }
};
然后我们打印其中的 title
:
console.log(fetchedUserData.job.title);
很明显,根据 fetchedUserData
,我们是可以成功地打印出 title
。但在实际开发中,fetchedUserData
中的部分数据有可能缺失,例如 job
的数据不存在,fetchedUserData
变成了:
const fetchedUserData = {
id: "1",
name: "Lebron"
};
这是如果我们再像上面那样去打印 title
,运行的时候就会报错。在 JavaScript 中,我们可以这样去解决问题:
console.log(fetchedUserData.job && fetchedUserData.job.title);
但在 TypeScript 中,我们使用可选链更为简洁:
console.log(fetchedUserData.job?.title);
在 Javascript 中,我们经常写类似下面的代码:
const input = null;
const storedData = input || "Default";
console.log(storedData);
上面的代码会输出 Default
。
如果 input
是空字符串,也会输出 Default
。但这可能不是我们想要的。我们想在当 input
是 null
或者 undefined
时,才默认设置为 Default
。在 TypeScript 中,我们可以使用 ??
来检查 null
或者 undefined
:
const input = "";
const storedData = input ?? "Default";
console.log(storedData);
??
只会检查 null
或者 undefined
,如果发现是其中一个,才会设置 Default
。