TypeScript 中允许定义联合类型,即指定某个变量可能为 A 类型也可能为 B 类型:
let stringOrNumber: string | number = 1;
stringOrNumber = 'hello';TypeScript 2.1 中为我们引入了 keyof 关键字,能够获取某个类型 T 的属性名列表,其返回结果也是联合类型,譬如:
interface Person {
name: string;
age: number;
location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string需要注意的是,keyof 关键字后面只能衔接 Interface 类型,如果我们希望获取某个正常的 JS 对象的键,那么需要先使用 typeof 关键字来获取到该对象对应的接口类型:
const Obj = {
a: 'b'
};
type K1 = keyof typeof Obj;这即是所谓的索引存取类型,或者搜索类型,我们经常会将其用于限制参数的输入值:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]; // Inferred type is T[K]
}
function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
obj[key] = value;
}
let x = { foo: 10, bar: 'hello!' };
let foo = getProperty(x, 'foo'); // number
let bar = getProperty(x, 'bar'); // string
let oops = getProperty(x, 'wargarbl'); // Error! "wargarbl" is not "foo" | "bar"
setProperty(x, 'foo', 'string'); // Error!, string expected number在实际开发中,我们往往只希望用到某个接口的部分属性,特别是在实体类的定义中:
interface UserModel {
email: string;
password: string;
address: string;
phone: string;
}
class User {
// 这里强制传入完全符合 UserModel 结构定义的对象,否则会抛出错误
update(user: UserModel) {
// Update user
}
}如果我们将接口属性定义为了可选属性,那么又会面临大量的空判断;TypeScript 2.1 之后为我们提供了 Partial 关键字,其内部的类型声明类似于:
type Partial<T> = { [P in keyof T]?: T[P] };我们可以用其声明部分校验:
class User {
update(user: Partial<UserModel>) {
// Update user
}
}
type ComponentConfig = {
optionOne: string;
optionTwo: string;
optionThree: string;
};
// 这里的使用场景是传入部分配置项
export class SomeComponent {
private _defaultConfig: Partial<ComponentConfig> = {
optionOne: '...'
};
}Partial 同样能够用于类的声明中:
type RectangleShape = Partial<Shape & Perimeter> & Point;TypeScript 还为我们提供了 Pick 与 Record 类型,Pick 类型允许我们定义仅包含目标类型中的部分属性:
// From T pick a set of properties K
declare function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K>;
const nameAndAgeOnly = pick(person, 'name', 'age'); // { name: string, age: number }泛型允许我们灵活地定义某些函数或者类接收的参数类型,更易于创建灵活而可控地可重用组件,泛型函数定义格式如下:
<T>(items :T[], callback :(item :T) => T) :T[]这里我们以简单地创建数组的函数为例:
function genericFunc<T>(argument: T): T[] {
var arrayOfT: T[] = []; // Create empty array of type T.
arrayOfT.push(argument); // Push, now arrayOfT = [argument].
return arrayOfT;
}
var arrayFromString = genericFunc<string>('beep');
console.log(arrayFromString[0]); // "beep"
console.log(typeof arrayFromString[0]); // String
var arrayFromNumber = genericFunc(42);
console.log(arrayFromNumber[0]); // 42
console.log(typeof arrayFromNumber[0]); // number// 接口泛型
interface Pair<T1, T2> {
first: T1;
second: T2;
}
// 泛型类属性
class Pair<T> {
fst: T;
snd: T;
}我们还可以指定泛型子类,即指定某个类型必须是实现某个接口或者继承自某个类:
interface HasLength {
length: number;
}
function addLengths<T extends HasLength>(t1: T, t2: T): number {
return t1.length + t2.length;
}
addLengths('hello', 'abc');
addLengths([1, 2, 3], [100, 11, 99]);TypeScript 2.3 之后支持泛型默认参数,可以某些场景减少函数类型重载的代码量,譬如:
declare function create<T extends HTMLElement = HTMLDivElement, U = T[]>(
element?: T,
children?: U
): Container<T, U>;