如: Person & Serializable & Loggable 返回的类型,同时拥有了这三个类型的成员。
这通常出现在 extend,mixins 中
interface A {
a: string;
}
interface B {
b: string;
}
let a = { a:'1' };
let b = {b:'1'};
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
extend(a,b)
即 T & U 即有 a 属性,也有 b 属性。
2. 联合类型
符号: |
表示几种类型之一,如 number | string | boolean 表示一个值可以是 number,string 或者 boolean类型
如果一个值的类型是联合类型,那么只能访问所有类型中共有的成员:
interface A {
a: number,
b: number,
}
interface B {
b: number,
c: number,
}
// let a: A | B = { a: 1, b: 2, };
// a.a; success, 因为 a 中确实有 a 属性
function getSmallPet(): A | B {
return { a: 2 } as A | B;
}
let pet = getSmallPet();
pet.a // errors
// a 属性在 B 中不存在,所以会报错,毕竟假如返回了B类型,那么就会报错
pet.b // okay
为了使以上代码不报错,我们可以使用类型断言:
let pet = getSmallPet();
(<A>pet).a
如果我们多次用到 pet.a, 那么就要使用多次类型断言
if((<A>pet).a) {
const c = (<A>pet).a;
}
因此引入了类型保护的概念
3. 类型保护
自定义类型保护:
要定义一个类型保护,只要定义一个函数,返回值是一个类型谓词即可:
function isFish(pet: A | B): pet is A {
return (<A>pet).a !== undefined;
}
pet is A就是类型谓词,谓词为 parameterName is Type, parameterName 必须是来自当前函数签名里的参数名。
这样使用变量调用isFish时,TypeScript会将变量缩减为 is 之后的那个类型,只要该类型与变量的原始类型兼容。
function isFish(pet: A | B): pet is A {
return (<A>pet).a !== undefined;
}
let a:A | B = {a:1,b:2}
if (isFish(a)) {
// 如果返回 true,那么 a 就是 A 类型
console.log(a.a)
} else {
// 否则就是 B 类型
console.log(a.c);
}
let s = "foo"
s = null; // 错误,
let s:string | null = "foo"
s = null; // 正确
s = undefined; // 错误
let s:string | null | undefined = "foo"
s = null; // 正确
s = undefined; // 正确
function broken(name: string | null): string {
function postfix(epithet: string) {
return name!.charAt(0) + '. the ' + epithet; // ok
}
name = name || "Bob";
return postfix("great");
}
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if(typeof n === 'string') {
return n;
} else {
return n();
}
}
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(easing: Easing) {
if (easing === "ease-in") {
// ...
}
else if (easing === "ease-out") {
}
else if (easing === "ease-in-out") {
}
else {
// error! should not pass null or undefined.
}
}
}
let button = new UIElement();
button.animate("ease-in");
button.animate("uneasy"); // 错误,限制了只能使用 "ease-in","ease-out","ease-in-out" 三个之一
字符串字面量类型还可以用于区分函数重载:
function createElement(tagName: "img"): HTMLImageElement; 图片元素
function createElement(tagName: "input"): HTMLInputElement; Input元素
// ... more overloads ...
function createElement(tagName: string): Element {
// ... code goes here ...
}
数字字面量类型
function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
// ...
}
一般很少像上面代码一样直接使用,但是可以用在缩小范围 debug 的时候
function foo(x: number) {
if (x !== 1 || x !== 2) {
// 报错,因为 1 和 2 没有交集。因此以上代码一定是 true
// number 不可能 === 1 且 === 2
}
}
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
}
}
完整性检查
多态的 this类型
class BasicCalculator {
public constructor(protected value: number = 0) { }
public currentValue(): number {
return this.value;
}
// 返回 this 类型,
public add(operand: number): this {
this.value += operand;
return this;
}
public multiply(operand: number): this {
this.value *= operand;
return this;
}
// ... other operations go here ...
}
当前 this 为 BasicCalculator 类型,当继承时, this 为子类类型
7. 索引类型
索引类型查询: keyof
索引类型访问: T[K]
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
使用:
type PersonPartial = Partial<Person>; // 得到可选类型
type ReadonlyPerson = Readonly<Person>; // 得到只读类型
更复杂的映射,为每个属性添加代理
type Proxy<T> = {
get(): T;
set(value: T): void;
}
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>;
}
function proxify<T>(o: T): Proxify<T> {
// ... wrap proxies ...
}
let proxyProps = proxify(props);
Readonly<T> 和 Partial<T> 用处不小,因此它们与 Pick 和 Record 一同被包含进了TypeScript的标准库里:
Pick
// 根据对象上的几个属性,得到一个新的类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
type Flags = {
option1: boolean;
option2: boolean;
}
type A = Pick<Flags,'option1'>
A 为 {
option1: boolean;
}