类型约束扩展
交叉类型是将多个类型合并为一个类型, 我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用
联合类型表示一个值可以是几种类型之一
联合类型存在一个问题,只能访问共有成员,这显然不合理。我们怎么访问实际实例的成员呢?
function getSmallPet(): Fish | Bird {
// ...
}
if ((
2. 自定义的类型保护
要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个 类型谓词:
每当使用一些变量调用 isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。
```typescript
// pet is Fish就是类型谓词
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
// 类型谓词
if (isFish(pet)) {
// 每当使用一些变量调用 isFish时,TypeScript会将变量缩减为那个具体的类型,只个类型与变量的原始类型是兼容的。
// 不需要断言
pet.swim();
} else {
// 注意TypeScript不仅知道在 if分支里 pet是 Fish类型; 它还清楚在 else分支里,一定 不是 Fish类型,一定是 Bird类型。
pet.fly();
}
function isNumber(x: any): x is number {
return typeof x === "number";
}
function isString(x: any): x is string {
return typeof x === "string";
}
function padLeft(value: string, padding: string | number) {
if (isNumber(padding)) {
return Array(padding + 1).join(" ") + value;
}
if (isString(padding)) {
// 类型谓词分支中,可以确认使用对应类型的操作
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
原文: 『然而,必须要定义一个函数来判断类型是否是原始类型,这太痛苦了。 幸运的是,现在我们不必将 typeof x === “number”抽象成一个函数,因为TypeScript可以将它识别为一个类型保护。 也就是说我们可以直接在代码里检查类型了。』
if 可以, switch 貌似有点问题
于是我们可以直接这么写:
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 只能识别基础类型 “number”, “string”, “boolean”或 “symbol”, 对象基本搞不定。
上述「2. 自定义的类型保护」的实现就可以简化为如下代码:
// 3. 自动类型保护机制
// instanceof 触发类型保护
if (pet instanceof Fish) {
// 类型细化为 Fish
pet.swim();
} else {
// 类型细化为 Bird
pet.fly();
}
。。。 尼玛,有这么好的方法为什么不早点拿出来。手动抓狂
TypeScript具有两种特殊的类型, null和 undefined,默认情况下,类型检查器认为 null与 undefined可以赋值给任何类型。 null与 undefined是所有其它类型的一个有效值。
null的发明者,Tony Hoare,称它为 价值亿万美金的错误。
let n: string = null;
let z: string = undefined;
n = 'n';
n = null;
注意,按照JavaScript的语义,TypeScript会把 null和 undefined区别对待。 string | null, string | undefined和 string | undefined | null是不同的类型。 |
类型保护和类型断言 [本章节 identifier! 这个后缀!号的语法没看明白在干什么。]
类型别名
起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型。 给原始类型起别名通常没什么用,尽管可以做为文档的一种形式使用。
type Name = string;
// 泛型别名
type NameResolver<T> = () => T;
type NameOrResolver = Name | NameResolver<Name>;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
const test = getName('test');
const testFn = getName(() => 'testFn');
console.log(test, ', ', testFn);
然而,类型别名不能出现在声明右侧的任何地方。
type Yikes = Array<Yikes>; // error
因为 软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,你应该尽量去使用接口代替类型别名。
并非语法层面,而是处理具体问题的技巧
实际上在继承结构中, this 依旧是动态寻址,依赖具体实现, 被类型约束。原来怎么用这里怎么用
索引类型: 参考索引类型(Index types) 章节
映射类型: TODO