[Translated] TypeScript 3.0: unknown type

TypeScript 3.0 introduces a new unknowntype, which is anycorresponding to the type of security type.

unknownAnd anythe main difference is that unknowntype will be more strict: On the unknownBefore most value type of operation, we have some form of inspection. In the pair anybefore the operation value types, we do not carry out any checks.

This article focuses on the unknownpractical application type, and contains the anykind of comparison. If a more comprehensive code example to understand unknownthe type of semantics, Anders Hejlsberg can see the original request for pulling .

any Types of

Let's first look at anythe type, so that we can better understand the introduction of unknownthe motivation behind the type.

Since TypeScript released the first version in 2012 anytype it has been in existence. It represents all possible values of JavaScript - basic types, objects, arrays, functions, Error, Symbol, and any value you may define.

In TypeScript, any type can be classified as any type. This makes anythe type of system has become a type of top-level type (also referred to as a global super type ).

This is something we assigned to the anycode sample types:

let value: any;

value = true;             // OK
value = 42;               // OK
value = "Hello World";    // OK
value = [];               // OK
value = {};               // OK
value = Math.random;      // OK
value = null;             // OK
value = undefined;        // OK
value = new TypeError();  // OK
value = Symbol("type");   // OK
复制代码

anyType system is an escape chamber of the type in nature. As a developer, this gives us a lot of freedom: TypeScript allows us to anyvalue types do anything, without first performing any form of censorship.

In the above example, the variables valueare defined as types any. Is, therefore, TypeScript think all of the following types of operations are correct:

let value: any;

value.foo.bar;  // OK
value.trim();   // OK
value();        // OK
new value();    // OK
value[0][1];    // OK
复制代码

Under so many scenarios, such mechanisms are too loose. Use anytype, you can easily write the correct type but the abnormality code. If we use anytype, you will not enjoy TypeScript a large number of protection mechanisms.

But if there is a default top-level type can be kept safe? This is the unknownreason for coming.

unknown Types of

Like all types can be classified as any, all types also can be classified as unknown. This makes it unknownbe another type TypeScript top type system (the other is any).

This is the same set of assignment examples we have seen before, this time using type unknownvariables:

let value: unknown;

value = true;             // OK
value = 42;               // OK
value = "Hello World";    // OK
value = [];               // OK
value = {};               // OK
value = Math.random;      // OK
value = null;             // OK
value = undefined;        // OK
value = new TypeError();  // OK
value = Symbol("type");   // OK
复制代码

For valueall variable assignments are considered to be the correct type.

When we try to type unknownwhen assigning values to other types of variables What will happen?

let value: unknown;

let value1: unknown = value;   // OK
let value2: any = value;       // OK
let value3: boolean = value;   // Error
let value4: number = value;    // Error
let value5: string = value;    // Error
let value6: object = value;    // Error
let value7: any[] = value;     // Error
let value8: Function = value;  // Error
复制代码

unknownType can only be assigned to anytype and unknowntype itself. Intuitively, this makes sense: only be able to save any type of container value to save unknownvalue types. After all, we do not know the variables valueare stored in what type of value.

Now let's see when we try to type unknownthe value of performing an operation what happens. The following operation is the same as we've seen before:

let value: unknown;

value.foo.bar;  // Error
value.trim();   // Error
value();        // Error
new value();    // Error
value[0][1];    // Error
复制代码

The valuevariable type is set unknownlater, these operations are no longer considered to be the correct type. By changing the anytype to unknowntype, has become our default setting allows almost anything does not allow everything from flip-style change.

This is the unknowntype of main value propositions: TypeScript does not allow us to type unknownvalues to perform any operations. Instead, we must first perform some type checking to narrow the range of values of the type we are using.

Narrow unknownType Range

We can in different ways be unknownthe type more specific narrow range of types, including typeofoperators, instanceofoperators, and custom type protection function. All of these types of narrow ranges of technologies contribute TypeScript based on the type of control flow analysis .

The following example shows valuehow the two ifget more specific type statement branch:

function stringifyForLogging(value: unknown): string {
  if (typeof value === "function") {
    // Within this branch, `value` has type `Function`,
    // so we can access the function's `name` property
    const functionName = value.name || "(anonymous)";
    return `[function ${functionName}]`;
  }

  if (value instanceof Date) {
    // Within this branch, `value` has type `Date`,
    // so we can call the `toISOString` method
    return value.toISOString();
  }

  return String(value);
}
复制代码

In addition to the use typeofor instanceofaddition operator, we can also use a custom type of the protecting function narrow unknownrange of types:

/**
 * A custom type guard function that determines whether
 * `value` is an array that only contains numbers.
 */
function isNumberArray(value: unknown): value is number[] {
  return (
    Array.isArray(value) &&
    value.every(element => typeof element === "number")
  );
}

const unknownValue: unknown = [15, 23, 8, 4, 42, 16];

if (isNumberArray(unknownValue)) {
  // Within this branch, `unknownValue` has type `number[]`,
  // so we can spread the numbers as arguments to `Math.max`
  const max = Math.max(...unknownValue);
  console.log(max);
}
复制代码

Although unknownValuethat has been classified as a unknowntype, please note that it is still how to get in if the branch number[]type.

Of unknowntype using the type of assertion

In the previous section, we have seen how to use typeof, instanceofand customize the type of protection function to convince TypeScript compiler has a value of some type. This is a designated "unknown" type to a more specific type of security and recommended approach.

If you want to force the compiler to trust type unknownof the value of a given type, you can use something like this type of assertion:

const value: unknown = "Hello World";
const someString: string = value as string;
const otherString = someString.toUpperCase();  // "HELLO WORLD"
复制代码

Please note, TypeScript in fact did not do anything special checks to make sure the type of assertion in fact valid. Type checker assumes that you know more about any type you use and believe in the type of assertion is correct.

If you make a mistake and specify the type of error, which can easily result in an error at runtime:

const value: unknown = 42;
const someString: string = value as string;
const otherString = someString.toUpperCase();  // BOOM
复制代码

This valuevariable is a numeric value, but we assume it is a string and use the type of assertion value as string. Use it with caution types assertion!

Joint types unknownType

Now let's look at how to handle union types in unknowntype. In the next section, we will also learn cross type.

In union types, the unknowntype will absorb any type. This means that if either type is composed of unknownjoint type will be the equivalent of unknown:

type UnionType1 = unknown | null;       // unknown
type UnionType2 = unknown | undefined;  // unknown
type UnionType3 = unknown | string;     // unknown
type UnionType4 = unknown | number[];   // unknown
复制代码

An accident of this rule is anythe type. If the type is composed of at least one anyjoint type will be the equivalent of any:

type UnionType5 = unknown | any;  // any
复制代码

So why unknowncan absorb any type ( anyexcept type)? Let's think about unknown | stringthis example. This type can represent any type or string type value of unkown. As we learned before, the value of all types can be defined as unknowntype, including all stringtypes, therefore, unknown | stringis to represent and unknowntype itself same set of values. Therefore, the compiler could be simplified type of joint unknowntype.

CROSS types unknownType

In the cross type, it can be any type of absorption unknowntype. This means that any type of unknownintersection will not change the result type:

type IntersectionType1 = unknown & null;       // null
type IntersectionType2 = unknown & undefined;  // undefined
type IntersectionType3 = unknown & string;     // string
type IntersectionType4 = unknown & number[];   // number[]
type IntersectionType5 = unknown & any;        // any
复制代码

Let's recap IntersectionType3: unknown & stringtype represents everything that can be assigned to the same time unknownand stringvalue types. Since each type can be assigned to unknownthe type, the type contained in the intersection unknowndoes not change the result. We will only stringtypes.

Use of type unknownvalue operator

unknownValue type can not be used as operands of most operators. This is because if we do not know the type of value we are using, most operators are unlikely to produce meaningful results.

You can type unknownused on the value of the operator only four equality and inequality operators:

  • ===
  • ==
  • !==
  • !=

If you want to type unknownusing any other operator value, you must specify the type (or types to use assertion to force the compiler to trust you).

Example: From localStoragereading the JSON

This is how we use unknownreal examples of the type.

Suppose we want to write a slave localStorageand anti-serialized to JSON functions read value. If the item does not exist or is not valid JSON, the function should return incorrect results, otherwise, it should deserialize and return values.

Because we do not know when deserializing JSON string persistence of what type of value we would get. We will use unknownas an anti-serialized value types. This means that we function call must be some form of checks (or use a type of assertion) prior to the return value of the operation.

Here we show how to achieve this function:

type Result =
  | { success: true, value: unknown }
  | { success: false, error: Error };

function tryDeserializeLocalStorageItem(key: string): Result {
  const item = localStorage.getItem(key);

  if (item === null) {
    // The item does not exist, thus return an error result
    return {
      success: false,
      error: new Error(`Item with key "${key}" does not exist`)
    };
  }

  let value: unknown;

  try {
    value = JSON.parse(item);
  } catch (error) {
    // The item is not valid JSON, thus return an error result
    return {
      success: false,
      error
    };
  }

  // Everything's fine, thus return a success result
  return {
    success: true,
    value
  };
}
复制代码

Return type Resultis a combined type is marked . In other languages, it can also be referred to Maybe, Optionor Optional. We used Resultto clear the result of successful and unsuccessful operations simulation.

tryDeserializeLocalStorageItemFunction calls are attempting to use valueor errormust first check the property before the successproperty:

const result = tryDeserializeLocalStorageItem("dark_mode");

if (result.success) {
  // We've narrowed the `success` property to `true`,
  // so we can access the `value` property
  const darkModeEnabled: unknown = result.value;

  if (typeof darkModeEnabled === "boolean") {
    // We've narrowed the `unknown` type to `boolean`,
    // so we can safely use `darkModeEnabled` as a boolean
    console.log("Dark mode enabled: " + darkModeEnabled);
  }
} else {
  // We've narrowed the `success` property to `false`,
  // so we can access the `error` property
  console.error(result.error);
}
复制代码

Note that tryDeserializeLocalStorageItemthe function can not simply by returning nullrepresented deserialized failure, for the following reasons:

  1. nullValue is a valid JSON value. Therefore, we can not distinguish between the values nullwere deserialized or because of a lack of parameters or syntax errors cause the entire operation to fail.
  2. If we return from the function null, we can not return an error at the same time. Therefore, we call those functions do not know why the operation failed.

For completeness, a more sophisticated alternative to this approach is to use the type of decoder for secure JSON parsing. We expect the decoder needs to specify the data structure value deserialized. If persistent JSON results do not match the data structure, the decoding failure will be well-defined way. In this way, our function always returns valid or failure of decoding result, you no longer need to unknowntype up.

If you find there is a translation error or other areas for improvement, welcome to Denver translation program to be modified and translations PR, also obtained the corresponding bonus points. The beginning of the article Permalink article is the MarkDown the links in this article on GitHub.


Nuggets Translation Project is a high-quality translation of technical articles Internet community, Source for the Nuggets English Share article on. Content covering Android , iOS , front-end , back-end , block chain , product , design , artificial intelligence field, etc., you want to see more high-quality translations, please continue to focus Nuggets translation program , the official micro-blog , we know almost columns .

Reproduced in: https: //juejin.im/post/5d04ac745188250a8b1fd203

Guess you like

Origin blog.csdn.net/weixin_33716557/article/details/93177689