More literal types and built-in type declarations

Time:2020-6-4

By Marius Schulz
Translator: front-end wit
Source: Marius Schulz


Alibaba cloud has been doing activities recently, with a 20% discount. I really think it’s cost-effective. You can click on this content or link to participate:
https://promotion.aliyun.com/…

Tencent cloud is currently doing activities, with 100 cloud products as low as 10% discount. You can click this content or link to participate


In order to ensure readability, this paper adopts free translation instead of literal translation.

Typescript 1.8 introduces the string literal type, which limits variables to a finite set of possible string values. In typescript 2.0, literal types are no longer limited to strings. The following literal types have been added to the type system:

  • Boolean literal type
  • Number literal type
  • Enum literal type

Next, let’s look at some examples of this type.

Boolean literal type

The following example defines two constantsTRUEandFALSE, they holdtrue andfalse Value:

const TRUE: true = true; // OK
const FALSE: false = false; // OK

Attempting to assign the opposite Boolean value to each local variable results in a type error:

const TRUE: true = false;
// Error: Type 'false' is not assignable to type 'true'

const FALSE: false = true;
// Error: Type 'true' is not assignable to type 'false'

With the introduction of Boolean literal types, predefined Boolean types are now equivalent totrue | falseUnion type of:

let value: true | false; // Type boolean

Although Boolean literal types are rarely useful in isolation, they are useful in combination with tag union types and control flow based type analysis. For example, you can define a genericResult <T>Type that either contains a type ofTEither contains a value of typestringError message for, as follows

type Result<T> =
  | { success: true; value: T }
  | { success: false; error: string };

This is a function that takes parameters:

function parseEmailAddress(
  input: string | null | undefined
): Result<string> {
  //If input is null, undefined or empty string   
  //(all false values) is returned directly.
  if (!input) {
    return {
      success: false,
      error: "The email address cannot be empty."
    };
  }

  //We only check whether the input matches the pattern   
  // <something> @ <something> . <something>   
  //Keep it simple and verify your email address correctly 
  if (!/^\[email protected]\S+\.\S+$/.test(input)) {
    return {
      success: false,
      error: "The email address has an invalid format."
    };
  }

  return {
    success: true,
    value: input
  };
}

Please note, enablestrictNullChecksAfter the option,stringNo waynullType of. In order to make theinputParameter acceptance can benullValue of type, which must be explicitly included in the union typenullandundefinedType.

We can now call it as followsparseEmailFunction

const parsed = parseEmailAddress("[email protected]");

if (parsed.success) {
  parsed.value; // OK
  parsed.error; // Error
} else {
  parsed.value; // Error
  parsed.error; // OK
}

Note that some attribute access expressions are underlined with red wavy lines:

More literal types and built-in type declarations

The advantage of this is that the compiler is only checkingparsed.successWe will be allowed to use it latervalueorerrorProperties:

  • Ifparsed.successbytrue, thenparsedMust be of type{ success: true; value: string }。 In this case, we can visitvalue, but not accessibleerror
  • Ifparsed.successbyfalse, thenparsedMust be of type{ success: false; error: string }。 In this case, we can visiterror, but not accessiblevalue

Number literal type

Similar to string literal type, we can restrict numerical variables to a finite set of known values

let zeroOrOne: 0 | 1;

zeroOrOne = 0;
// OK

zeroOrOne = 1;
// OK

zeroOrOne = 2;
//Error: type '2' cannot be assigned to type '0 | 1'

In practice, we can use numeric literals when dealing with port numbers. Insecure HTTP use port80, while HTTPS uses ports443。 We can write agetPortFunction and encode only two possible return values in its function signature

function getPort(scheme: "http" | "https"): 80 | 443 {
  switch (scheme) {
    case: "http":
      return 80;
    case: "https":
      return 443;
  }
}

const httpPort = getPort('http'); // Type 80 | 443

It’s even more interesting if we combine literal types with typescript function overloading. In this way, we cangetPortDifferent overloads of functions provide more specific types.

function getPort(scheme: "http"): 80;
function getPort(scheme: "https"): 443;
function getPort(scheme: "http" | "https"): 80 | 443 {
  switch (scheme) {
    case "http":
      return 80;
    case "https":
      return 443;
  }
}

const httpPort = getPort("http"); // Type 80
const httpsPort = getPort("https"); // Type 443

Now, when the returned value will never be the same as the compared value, the editor will prompt us, for examplehttpPortAnd value443When comparing:

More literal types and built-in type declarations

becausehttpPortThe type of is80, so it always contains values80Of course, the value will never be equal to the value443。 In this case, typescript compiler can help us detect wrong logic and invalid code.

Enum literal type

Finally, we can use enumeration as literal type. Continue with the previous example to implement a given port(80or443)Map to the corresponding scheme (respectivelyHTTPorHTTPS)Function of. To this end, we first declare aconst enum, which builds two port numbers:

const enum HttpPort {
  Http = 80,
  Https = 443
}

Now it’sgetSchemeFunction, use function overload again to implement special type annotation:

function getScheme(port: HttpPort.Http): "http";
function getScheme(port: HttpPort.Https): "https";
function getScheme(port: HttpPort): "http" | "https" {
  switch (port) {
    case HttpPort.Http:
      return "http";
    case HttpPort.Https:
      return "https";
  }
}

const scheme = getScheme(HttpPort.Http);
// Type "http"

Constant enumeration has no runtime representation (unless providedpreserveConstEnumsCompiler options), that is,enumConstant values for use cases will be inlined wherever they are used. Here is the compiled JS code, with the annotations removed:

function getScheme(port) {
  switch (port) {
    case 80:
      return "http";
    case 443:
      return "https";
  }
}
var scheme = getScheme(80);

Is it super simple?

Typescript 2.0 gives us more granular control over which built-in API declarations are included in the project. Previously, ES6 related packages could only be accessed if they were configured inES6 Api。 Now that the built-in standard library declaration is modular, typescript allows us to choose which type declaration to include.

–Lib compiler options

The type declaration of JS standard library is divided into a groupAPIGroup. At the time of writing in late November 2016, the following groups were defined

  • dom
  • webworker
  • es5
  • es6 / es2015
  • es2015.core
  • es2015.collection
  • es2015.iterable
  • es2015.promise
  • es2015.proxy
  • es2015.reflect
  • es2015.generator
  • es2015.symbol
  • es2015.symbol.wellknown
  • es2016
  • es2016.array.include
  • es2017
  • es2017.object
  • es2017.sharedmemory
  • scripthos

Through--libCommand line options ortsconfig.jsonInlibProperty to pass any subset of the above groups to the typescript compiler. Typescript will inject only the types you specify; that is, it will treat all other API groups as non-existent in your environment.

If not explicitly providedlibOption, typescript will implicitly inject the API groups required for web development.

be careful: if--libNo default library specified. The default library is

  • For –target ES5:["dom", "es5", "scripthost"]
  • For –target ES6: ["dom", "es6", "dom.iterable", "scripthost"]

Using ES6 promise in typescript project with Es5 as target

Suppose you’re dealing with atargetbyes5Project to run in all major browsers. Yourtsconfig.jsonIt could be this:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

becauselibOption is not specified, so typescript is injected into the API Group by default"dom""es5"and"scripthost"。 Now you want to use the nativePormise。 These are not in Es5, so we need to install onepolyfillTo let our code run in the old browser:

npm install --save es6-promise

You can then import the corresponding library in the import file

import "es6-promise";

With this Polyfill, you can now use it in your applicationPromise, the code can also run normally. However, typescript gives you a compile time error:Cannot find the name 'Promise'。 that is becausePromiseThe type declaration of is not included in any injected API Group.

More literal types and built-in type declarations

Let typescript knowPromiseIt will exist at runtime, which islibWhere compiler options work:

More literal types and built-in type declarations

Note that once the default is overridden, all API groups must be explicitly provided as follows:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "noImplicitAny": true,
    "strictNullChecks": true,
    "lib": ["dom", "es5", "es2015.promise"]
  }
}

Now the editor is not reporting errors:

More literal types and built-in type declarations


The bugs that may exist in the editing cannot be known in real time. In order to solve these bugs afterwards, a lot of time has been spent on log debugging. In this way, we recommend a good bug monitoring tool fundebug.

Original text:

https://mariusschulz.com/blog…
https://mariusschulz.com/blog…


communication

The articles of dry goods series are summarized as follows. I think it’s a good idea to order a star. Welcome to learn from each other.

https://github.com/qq449245884/xiaozhi

Because of the space limitation, today’s sharing is only here. If you want to know more about it, you can sweep the bottom two dimensional code of each article, then pay attention to our WeChat official account, and learn more information and valuable content.

More literal types and built-in type declarations