Object extension operator and rest operator and keyof and lookup type

TypeScript 2.1 adds support for object extension operation and rest attribute proposal, which is standardized in ES2018. Rest and spread attributes can be used in a type safe manner.

 

Object rest property

Suppose you have defined a simple literal object with three properties

const marius = {
  name: "Marius Schulz",
  website: "https://mariusschulz.com/",
  twitterHandle: "@mariusschulz"
};

Using ES6 deconstruction syntax, you can create several local variables to save the values of the corresponding attributes. TypeScript correctly infers the type of each variable:

const { name, website, twitterHandle } = marius;

name;          // Type string
website;       // Type string
twitterHandle; // Type string

These are all right, but this is nothing new until now. In addition to extracting a set of attributes of interest, you can also use The syntax collects all remaining attributes into the rest element:

const { twitterHandle, ...rest } = marius;

twitterHandle; // Type string
rest; // Type { name: string; website: string; }

TypeScript determines the correct type for the resulting local variable. Although the # twitter handle # variable is an ordinary string, the # rest # variable is an object that contains the remaining two properties that have not been deconstructed.

 

Object extension properties

Suppose we want to make an HTTP request using the {fetch() API. It accepts two parameters: a URL and an options object that contains any custom settings requested.

In an application, you can encapsulate the call to fetch() and provide default options and override specific settings for a given request. These configuration items are similar as follows:

const defaultOptions = {
  method: "GET",
  credentials: "same-origin"
};

const requestOptions = {
  method: "POST",
  redirect: "follow"
};

Using object extension, you can merge two objects into a new object and pass it to the {fetch() method

// Type { method: string; redirect: string; credentials: string; }
const options = {
  ...defaultOptions,
  ...requestOptions
};

Create a new object by extending the attribute of the object, copy all attribute values in "defaultOptions", and then copy all attribute values in requestOptions from left to right. The final results are as follows:

console.log(options);
// {
//   method: "POST",
//   credentials: "same-origin",
//   redirect: "follow"
// }

Note that the order of allocation is important. If a property appears in two objects at the same time, the latter will replace the former.

Of course, TypeScript understands this order. Therefore, if multiple extension objects define an attribute with the same key, the type of the attribute in the result object will be the last assigned attribute type, because it overrides the previously assigned attribute:

const obj1 = { prop: 42 };
const obj2 = { prop: "Hello World" };

const result1 = { ...obj1, ...obj2 }; // Type { prop: string }
const result2 = { ...obj2, ...obj1 }; // Type { prop: number }

 

Make a shallow copy of an object

Object extensions can be used to create shallow copies of objects. Suppose I want to create a new todo item from an existing todo item by creating a new object and copying all the attributes. Using the object can easily:

const todo = {
  text: "Water the flowers",
  completed: false,
  tags: ["garden"]
};

const shallowCopy = { ...todo };

In fact, you will get a new object, and all attribute values will be copied:

console.log(todo === shallowCopy);
// false

console.log(shallowCopy);
// {
//   text: "Water the flowers",
//   completed: false,
//   tags: ["garden"]
// }

You can now modify the text attribute, but not the original todo entry:

hallowCopy.text = "Mow the lawn";

console.log(shallowCopy);
// {
//   text: "Mow the lawn",
//   completed: false,
//   tags: ["garden"]
// }

console.log(todo);
// {
//   text: "Water the flowers",
//   completed: false,
//   tags: ["garden"]
// }

However, the new todo entry refers to the same # tags # array as the first one. Since it is a shallow copy, changing the array will affect these two todos

shallowCopy.tags.push("weekend");

console.log(shallowCopy);
// {
//   text: "Mow the lawn",
//   completed: false,
//   tags: ["garden", "weekend"]
// }

console.log(todo);
// {
//   text: "Water the flowers",
//   completed: false,
//   tags: ["garden", "weekend"]
// }

If you want to create a deep copy of a serialized object, consider using JSON Parse (JSON. Stringify (obj)) or other methods, such as object assign(). Object extensions only copy attribute values. If a value is a reference to another object, it may lead to unexpected behavior.

 

keyof and lookup type

JS is a highly dynamic language. Capturing the semantics of certain operations in a static type system can sometimes be tricky. Take a simple {prop} function as an example:

function prop(obj, key) {
  return obj[key];
}

It accepts an object and a key and returns the value of the corresponding property. Different attributes of an object can have completely different types. We don't even know what obj looks like.

So how do I write this function in TypeScript? Try it first:

With these two type annotations, obj must be an object and key must be a string. We have now limited the set of possible values of the two parameters. However, TS still infers that the return type is any:

const todo = {
  id: 1,
  text: "Buy milk",
  due: new Date(2016, 11, 31)
};

const id = prop(todo, "id");     // any
const text = prop(todo, "text"); // any
const due = prop(todo, "due");   // any

Without further information, TypeScript does not know which value will be passed for the {key} parameter, so it cannot infer the more specific return type of the prop function. We need to provide more type information to achieve this.

Guangzhou brand design companyhttps://www.houdianzi.com PPT template downloadhttps://redbox.wode007.com

keyof operation symbol

In JS, APIs with attribute names as parameters are quite common, but so far, there is no type relationship expressed in those APIs.

TypeScript 2.1 adds the {keyof} operator. Enter the index type query or , keyof. The type generated by the index type query keyof T is the attribute name of , T. Suppose we have defined the following Todo interface:

interface Todo {
  id: number;
  text: string;
  due: Date;
}

You can apply the 'keyof' operator to the 'Todo' type to obtain the type of all its attribute keys, which is the union of string literal types

type TodoKeys = keyof Todo; // "id" | "text" | "due"

Of course, you can also write the union type "id" | "text" | "due" manually instead of using | keyof, but it is troublesome, error prone and troublesome to maintain. Moreover, it should be a Todo specific solution, not a generic solution.

 

Index type query

With , keyof, we can now improve the type annotation of , prop , function. We no longer want to accept any string as the # key # parameter. On the contrary, we require that the parameter {key} actually exists on the type of the incoming object

function prop <T, K extends keyof T>(obj: T, key: K) {
  return obj[key]
}

TypeScript now infers that the return type of the # prop # function is # T[K], which is the so-called # index type query # or # search type. It represents the type of attribute K of type T. If you now access the following three attributes of {todo} through the {prop} method, each attribute has the correct type:

const todo = {
  id: 1,
  text: "Buy milk",
  due: new Date(2016, 11, 31)
};

const id = prop(todo, "id");     // number
const text = prop(todo, "text"); // string
const due = prop(todo, "due");   // Date

Now, what happens if you pass a key that doesn't exist on the todo object

The compiler will report an error, which is good. It prevents us from trying to read a non-existent attribute.

For another real-world example, check out the "lib. Lib" released with the TypeScript compiler es2017. object. d. TS # object in type declaration file Entries() method:

interface ObjectConstructor {
  // ...
  entries<T extends { [key: string]: any }, K extends keyof T>(o: T): [keyof T, T[K]][];
  // ...
}

The entries method returns an array of tuples, each containing an attribute key and a corresponding value. Admittedly, there are a lot of square brackets in the return type, but we have been looking for type security.

Tags: Javascript

Posted by ukphoto on Wed, 04 May 2022 07:06:37 +0300