一、交叉类型与联合类型
表示一个对象类型的并集与交集
1,交叉类型是将多个类型合并为一个类型,定义的对象必须包含所有的类型,使用“&”表示
2,联合类型:如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员,使用“|”表示
举例:
interface ia {
name: { attr1: string };
}
interface ib {
name: { attr2: number };
age: number;
}
// 交叉类型所有属性都要有
const jiaocha: ia & ib = { name: { attr1: "11", attr2: 22 }, age: 16 };
// 联合类型有其中一种属性就好了
const lianhe: ia | ib = { name: { attr1: "1" } };
二、类型保护与区分类型
如果想要访问这种json对象的方法swim不报错,有两种方法:
interface Bird {
fly: () => void;
layEggs: () => void;
}
interface Fish {
swim: () => void;
layEggs: () => void;
}
const getSmallPet = (): Fish | Bird => {
const swim = () => {
console.log("do swim");
};
const layEggs = () => {
console.log("do layEggs");
};
return { swim, layEggs };
};
// 为什么需要断言保护:因为访问报错
const pet = getSmallPet();
pet.layEggs(); // 正常访问:因为是交集所以可以访问
// pet.swim(); // 访问报错:交集之外,就访问不了,就需要用到类型保护区分类型
1,断言
// 优点,方便使用;缺点,冗余,如果少次访问的时候可以使用,且容易出错
(pet as Fish).swim();
2,用户自定义的类型保护
const isFish = (pet: Fish | Bird): pet is Fish => {
// 使用类型谓词,多次访问时使用
// pet is Fish就是类型谓词。 parameterName必须是来自于当前函数签名里的一个参数名。
return (<Fish>pet).swim !== undefined;
};
// 'swim' 和 'fly' 调用都没有问题了
if (isFish(pet)) {
pet.swim();
} else {
// 注意:TypeScript不仅知道在if分支里pet是Fish类型;它还清楚在else分支里,一定不是Fish类型,而是Bird类型。
pet.fly();
}
3,typeof类型保护
const padLeft = (value: string, padding: string | number) => {
// 缺点,只能访问简单类型,比如string、number、boolen;优点,方便
if (typeof padding === "number") {
console.log(Number(padding) + value);
}
if (typeof padding === "string") {
console.log(padding + value);
}
};
padLeft(" fish", 1);
padLeft(" fish", "one");
4,instanceof类型保护
interface Animal {
name(): string;
}
class MyFish implements Animal {
constructor(private numSpaces: number) {}
name() {
return String(this.numSpaces);
}
}
class MyBird implements Animal {
constructor(private value: string) {}
name() {
return this.value;
}
}
function getRandomAnimal() {
return Math.random() < 0.5 ? new MyFish(4) : new MyBird("fly Bird");
}
// 随机选一个类型,类型为SpaceRepeatingPadder | StringPadder
let padder: Animal = getRandomAnimal();
if (padder instanceof MyFish) {
const name = padder.name(); // 类型细化为'SpaceRepeatingPadder'
console.log("如果是MyFish===》", name);
}
if (padder instanceof MyBird) {
const name = padder.name(); // 类型细化为'StringPadder'
console.log("如果是MyBird===》", name);
}
三、可以为null或undefined的类型
类型检查器认为 null与 undefined可以赋值给任何类型。 null与 undefined是所有其它类型的一个有效值。
// 注意,按照JavaScript的语义,TypeScript会把 null和 undefined区别对待。 string | null, string | undefined和 string | undefined | null是不同的类型。
let s = "foo";
// s = null; // 错误, 'null'不能赋值给'string'
let sn: string | null = "bar";
sn = null; // 可以
// sn = undefined; // 错误, 'undefined'不能赋值给'string | null'
1,可选参数和可选属性
a,可选参数会被自动地加上 | undefined:
const add = (x: number, y?: number) => {
return x + (y || 0);
};
add(1, 2);
add(1);
add(1, undefined); // 直接设置undefined
// add(1, null); // 错误, 'null' 不能赋值给 'number | undefined'
b,可选属性也会有同样的处理:
class MyClass {
type?: number;
}
let myClass = new MyClass();
myClass.type = 13;
myClass.type = undefined; // ok
// c.b = null; // 错误, 'null' 不属于 'number | undefined'
2,类型保护和类型断言
a,由于可以为null的类型是通过联合类型实现,那么你需要使用类型保护来去除 null
。
const f1 = (sn: string | null): string => {
if (sn == null) {
return "default";
} else {
return sn;
}
};
f1(null);
const f2 = (sn: string | undefined): string => {
return sn || "default";
};
b,如果编译器不能够去除 null或 undefined,你可以使用类型断言手动去除。 语法是添加 !后缀: identifier!从 identifier的类型里去除了 null和 undefined:
const broken = (name: string | null): string => {
const postfix = (epithet: string) => {
//return name.charAt(0) + '. the ' + epithet; // 错误, 'name' 可能是 null
return name!.charAt(0) + ". the " + epithet; // 正确
};
name = name || "Bob";
return postfix("great");
};