内容简介:Previously, we’ve discussedGenerics are very useful in many situations. By usingA
Previously, we’ve discussed the basics of TypeScript Generics . This time, we take them to a higher level. In this article, we learn about index types . To do so, we also explore union types , the keyof keyword, and string literal types . Today we also learn mapped types and use them with conditional types.
Index types
Generics are very useful in many situations. By using index types, we can improve our types even further. To get to know them, let’s first learn what a union type and the keyof keyword are.
A union type represents one of several types. To separate them, we use a vertical bar.
const value: string | number;
String literal typesare often used with unions. A string literal can only be assigned a particular string value. It can be considered a subtype of a string type.
type UserRole = 'admin' | 'moderator' | 'author';
Now, let’s consider this simple interface:
interface User { id: number; name: string; email: string; role: UserRole; }
By using the keyof keyword, we can achieve a union of string literal types.
type UserKeysType = keyof User; // 'id' | 'name' | 'email' | 'role' ;
All of the above knowledge gives us quite a bit of flexibility.
Introducing index types
TypeScript 2.1 introduced index types . They look the same as accessing a property of an object but refer to types.
type IdType = User['id']; // number
We sometimes refer to index types as lookup types .
We can use the above in a dynamic manner. Let’s inspect this popular example:
const user: User = { id: 15, name: 'John', email: 'john@smith.com', role: 'admin' };
function getProperty<ObjectType, KeyType extends keyof ObjectType>( object: ObjectType, property: KeyType ): ObjectType[KeyType] { return object[property]; }
When we use our getProperty , the compiler checks if a string that we pass into it is an actual property of an object.
getProperty(user, 'property');
Argument of type ‘”property”‘ is not assignable to parameter of type ‘”id” | “name” | “email” | “role”‘.
When we return object [ property ] , the TypeScript compiler performs a type lookup. Thanks to that, the return type of the getProperty varies based on the passed string.
getProperty(user, 'id').toLowerCase();
Property ‘toLowerCase’ does not exist on type ‘number’.
Creating a Map from an object
A real-life example of the above might be converting an object to a Map. TypeScript aside, the most straightforward way to do this is to use Object.entries .
const settings = { isModalOpened: true, canDelete: false, role: 'Admin' } const settingsMap = new Map( Object.entries(settings) );
The above code, even though valid, does not produce the most detailed types.
settingsMap.get('role').toLowerCase()
Property ‘toLowerCase’ does not exist on type ‘string | boolean’.
The error above indicates that the return type of the settingsMap . get function is a union type 'string | boolean' . We know that the type of role is a string. Let’s fix that!
We can create our own interface that extends Map and provides more detailed typings.
interface MapFromObject<ObjectType, KeyType extends keyof ObjectType> extends Map<KeyType, ObjectType[KeyType]> { get: <PropertyType extends keyof ObjectType>(key: PropertyType) => ObjectType[PropertyType]; }
const settingsMap = new Map( Object.entries(settings) ) as MapFromObject<Settings, keyof Settings>;
Now, every time we use the get method, we get an exact type of property.
settingsMap.get('role').toLowerCase(); // 'admin'
If you have some other solution to the above issue, feel free to share it
If you need, you can also provide types for the set function in a similar manner.
Mapped types
The mapped types allow us to create new types from existing ones. A common use case is to make all of the properties of an object read-only .
type Readonly<ObjectType> = { readonly [KeyType in keyof ObjectType]: ObjectType[KeyType]; }
The above is such a common use-case that we now have a Readonly type built-in and ready to use.
An example of its usage is the Object.freeze function. Let’s look into how TypeScript handles it:
interface ObjectConstructor { /** * Prevents the modification of existing property attributes and values, * and prevents the addition of new properties. * @param o Object on which to lock the attributes. */ freeze<T>(o: T): Readonly<T>; }
As we can see, the Object.freeze function returns the object that is mapped using the Readonly modifier.
TypeScript developers identified more useful modifiers that might come in handy, such as Pick .
/** * 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]; };
Even though we have a set of modifiers for different occasions, we might need to write some custom ones. When doing so, conditional types might be of some use.
Conditional type selects one of two types based on a condition
type WithNumbersInsteadOfStrings<ObjectType> = { [PropertyType in keyof ObjectType]: ObjectType[PropertyType] extends number ? string : ObjectType[PropertyType]; };
const user: WithNumbersInsteadOfStrings<User> = { id: '15', name: 'John', email: 'john@smith.com', role: 'admin' };
Summary
In this article, we’ve expanded more on the subject of generics in TypeScript. We’ve investigated the indexed types and mapped types. When doing so, we’ve also learned the keyof keyword, the union types . We’ve also stumbled upon string literal types and conditional types . Learning all of the above will definitely expand our TypeScript knowledge!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web API的设计与开发
[日] 水野贵明 / 盛荣 / 人民邮电出版社 / 2017-6 / 52.00元
本书结合丰富的实例,详细讲解了Web API的设计、开发与运维相关的知识。第1章介绍Web API的概要;第2章详述端点的设计与请求的形式;第3章介绍响应数据的设计;第4章介绍如何充分利用HTTP协议规范;第5章介绍如何开发方便更改设计的Web API;第6章介绍如何开发牢固的Web API。 本书不仅适合在工作中需要设计、开发或修改Web API的技术人员阅读,对想了解技术细节的产品经理、运维人......一起来看看 《Web API的设计与开发》 这本书的介绍吧!
UNIX 时间戳转换
UNIX 时间戳转换
HSV CMYK 转换工具
HSV CMYK互换工具