这是我参与11月更文挑战的第 4 天,活动详情查看:2021最后一次更文挑战
前面我们学习了Typescript的基础类型、泛型和接口,能满足书写Typescript代码的大多数需求了。
比如有这样一个需求:定义一个MyInt接口,并创建相应的man对象。
interface MyInt {
name: string
age?: number
}
const man: MyInt = {
name: 'Tom',
age: 18
}
console.log(man)
假如后来我们想让name属性是只读的,那么我们可以怎么操作呢?
除了重新定义一个接口,有没有更方便的方法呢?
答案当然是有的。我们可以借助Typescript提供的Partial<T>、Required<T>和Readonly<T>等工具函数去实现。这些工具函数的源码可参考TypeScript 项目的./node_modules/typescript/lib/lib.es5.d.ts文件。
Partial<T>
把某个接口类型中定义的属性变成可选。是TypeScript 2.1版本发布的。
demo1. 改变接口中的属性
interface Person {
age: number;
name: string;
}
const jerry: Person = {
age: 10,
name: 'Jerry'
};
const tom: Partial<Person> = {
name: 'Tom'
};
console.log(jerry)
console.log(tom)
demo2. 对一个数据对象做局部更新
interface DataModel {
name: string
age: number
address: string
}
let store: DataModel = {
name: '',
age: 0,
address: ''
}
function updateStore (store: DataModel, payload: Partial<DataModel>): DataModel {
return {
...store,
...payload
}
}
store = updateStore(store, {
name: 'tom',
age: 18
})
console.log(store)
源码实现
type Partial<T> = {
[P in keyof T]?: T[P]
}
实现Partial<T>
interface Person {
age: number;
name: string;
}
const jerry: Person = {
age: 10,
name: 'Jerry'
};
type JerryPartial<T> = {
[P in keyof T]?: T[P];
};
const tom: JerryPartial<Person> = {
name: 'Tom'
};
Required<T>
把所有可选属性变成必选属性。
interface Props {
a?: number;
b?: string;
}
const obj: Props = { a: 5 };
// ok
const obj2: Required<Props> = { a: 5 };
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.
源码实现
type Required<T> = {
[P in keyof T]-?: T[P];
};
Readonly<T>
在 javascript 中,如果给 const 变量赋值为一个引用类型(比如一个对象),是可以修改属性值的,不能修改的是变量中存储的引用。如果要实现对象属性值的不可变,可以使用 Object.freeze。
Readonly<T>是将类型 T 中包含的属性设置为 readonly,并返回一个新类型。这里 readonly 是只读的意思,在初始化后就不能再修改值。可以结合 const 关键字实现引用类型属性值为常量的目的。
interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: "Delete inactive users",
};
todo.title = "Hello";
// Cannot assign to 'title' because it is a read-only property.
源码实现
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Record<Keys, Type>
定义一个对象的 key 的类型和 value 的类型。
比如我需要一个对象,有 ABC 三个属性,属性的值必须是数字,那么就这么写:
type keys = 'A' | 'B' | 'C'
const result: Record<keys, number> = {
A: 1,
B: 2,
C: 3
}
源码实现
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
这里
keyof any代表可以作为对象索引的任何值,相当于string | number | symbol。
Pick<Type, Keys>
从类型 Type 中,挑选一组属性组成一个新的类型返回。这组属性由 Keys 限定, Keys 是字符串或者字符串并集。
interface Person {
name: string
age: number
id: string
}
type man = Pick<Person, 'name' | 'age'>
let a: man = {
name: 'hh',
age: 18
}
console.log(a)
源码实现
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
K extends keyof T ,表示 K 是 keyof T 的子集。返回的类型的键需要满足[P in K],值类型满足T[P]。
Exclude<Type, ExcludedUnion>
Constructs a type by excluding from Type all union members that are assignable to ExcludedUnion.
声明一个type,在Type 中排除了ExcludedUnion的所有成员。
type T0 = Exclude<"a" | "b" | "c", "a">;
// type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
// type T2 = string | number
源码实现
type Exclude<T, U> = T extends U ? never : T;
这里的
T extends U表示条件类型,当T“继承了”U时,返回never,否则返回T。
Extract<Type, Union>
提取Type中可分配到Union的所有成员。
type ExtractedType = Extract<"x" | "y" | "z", "x" | "y">;
// "x" | "y"
type T1 = Extract<string | number | (() => void), Function>;
// type T1 = () => void
用来提取两个类型的公有属性名会非常的合适:
interface Human {
id: string;
name: string;
surname: string;
}
interface Cat {
id: string;
name: string;
sound: string;
}
type CommonKeys = Extract<keyof Human, keyof Cat>; // "id" | "name"
源码实现
type Extract<T, U> = T extends U ? T : never;
Omit<Type, Keys>
构造一个类型,这个类型包含类型 Type中除了 Keys 之外的其余属性。 Keys是一个字符串或者字符串并集。
interface Person {
name: string
age: number
work: string
}
type PersonNoWork = Omit<Person, 'work'>
const man: PersonNoWork = {
name: "hh",
age: 18
};
console.log(man);
源码实现
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = {
[P in Exclude<keyof T, K>]: T[P];
}
// or
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
NonNullable<Type>
Constructs a type by excluding null and undefined from Type.
type T0 = NonNullable<string | number | null | undefined>;
// type T0 = string | number
源码实现
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;