类型操作
通过组合各种类型操作符,我们可以以简洁、可维护的方式表达复杂的操作和值。在本节中,我们将介绍根据现有类型或值来表达新类型的方法。
- 泛型 - 带参数的类型
- keyof 类型操作符 - 使用keyof操作符创建新类型
- typeof 类型操作符 - 使用typeof操作符创建新类型
- 索引访问类型 - 使用Type['a']语法访问类型的子集
- 条件类型 - 类型类似于类型系统中的 if 语句
- 映射类型 - 通过映射现有类型中的每个属性来创建类型
- 模板文字类型 - 通过模板文字字符串更改属性的映射类型
1 泛型
指在定义函数、接口、类或类型的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
泛型函数
function createArray(length: number, value: any): Array<any> {
let result = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
泛型接口
interface Config<T>{
(value:T):T;
}
function getData<T>(value:T):T{
return value;
}
var myGetData:Config<string> = getData;
myGetData('10');
泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
泛型类型
function identity<Type>(arg: Type): Type {
return arg;
}
let myIdentity: <Type>(arg: Type) => Type = identity;
2 keyof
keyof T
表示类型T所有公共属性的字面量的联合类型,其返回类型是一个联合类型。
const COLORS = {
red: 'red',
blue: 'blue'
}
// 首先通过typeof操作符获取Colors变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue'
type Colors = keyof typeof COLORS
let color: Colors;
color = 'red' // Ok
color = 'blue' // Ok
// Type '"yellow"' is not assignable to type '"red" | "blue"'.
color = 'yellow' // Error
3 typeof
TypeScript 扩展了 typeof 的作用,
JavaScript 中的 typeof,用法为 typeof 变量名
或者 typeof 值
,返回一个字符串,如 string
。
let message = 'hello'
console.log(typeof message)
console.log(typeof 'hello')
TypeScript 扩展的 typeof,用法为 type T = typeof 变量名
,返回一个类型。
let message = 'hello'
type t1 = typeof message
type t2 = typeof typeof 'hello' // 报错 Identifier expected
4 索引访问类型
T[K]
表示对象 T 的属性K所表示的类型
let person = {
name: 'musion',
age: 35
}
function getValues(person: any, keys: string[]) {
return keys.map(key => person[key])
}
console.log(getValues(person, ['name', 'age'])) // ['musion', 35]
console.log(getValues(person, ['gender'])) // [undefined]
在上述例子中,可以看到 getValues(persion, ['gender'])
打印出来的是[undefined]
,但是 TS 编译器并没有给出报错信息,那么如何使用 TS 对这种模式进行类型约束呢?这里就要用到了索引访问类型,改造一下 getValues 函数:
function getValues<T, K extends keyof T>(person: T, keys: K[]): T[K][] {
return keys.map(key => person[key]);
}
interface Person {
name: string;
age: number;
}
const person: Person = {
name: 'musion',
age: 35
}
getValues(person, ['name']) // ['musion']
getValues(person, ['gender']) // 报错:
// Argument of Type '"gender"[]' is not assignable to parameter of type '("name" | "age")[]'.
// Type "gender" is not assignable to type "name" | "age".
5 条件类型
SomeType extends OtherType ? TrueType : FalseType;
条件类型有点像 JavaScript 中的三元表达式,当左侧的类型可分配给右侧的类型时,将在第一个分支(“真”分支)中获得类型;否则,将在后一个分支(“假”分支)中获得类型。
interface Animal {
live(): void;
}
interface Dog extends Animal {
woof(): void;
}
type Example1 = Dog extends Animal ? number : string; // type Example1 = number
type Example2 = RegExp extends Animal ? number : string; // type Example2 = string
6 映射类型
TypeScript 提供了从旧类型中创建新类型的一种方式 — 映射类型。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。TS 内置了一些映射类型(实际上是一些语法糖),让我们可以方便地进行类型映射,可以在 TypeScript 包中的 typescript/lib/lib.es5.d.ts 中找到他们的定义:
// 将传入的属性变为只读选项
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
// 将传入的属性变为可选项:keyof T 拿到 T 所有属性名, 然后 in 进行遍历, 将值赋给 P, 最后 T[P] 取得相应属性的值.
type Partial<T> = {
[P in keyof T]?: T[P];
}
// 使用
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
7 模板文字类型
比如参数枚举值如下:
"top-left" | "top-center" | "top-right" | "middle-left" | "middle-center" | "middle-right" | "bottom-left" | "bottom-center" | "bottom-right"
如果全量列举每一个值,不仅繁琐,而且容易出错,此时模板文字类型就派上用场了:
type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";
declare function setToolTipPosition(value: `${VerticalAlignment}-${HorizontalAlignment}`): void;
setToolTipPosition("top-left"); // ok!
setToolTipPosition("top-middel"); // error!
setToolTipPosition("top-pot"); // error!