Common types of TypeScript

The official documents of TypeScript have already been updated, but the Chinese documents I can find are still in the older version. Therefore, some newly added and revised chapters are translated and sorted out.

This translation is compiled from "TypeScript Handbook" Everyday Types "Chapter.

This article is not translated strictly according to the original text, and some contents are explained and supplemented.

Type Aliases

We have learned to use object types and union types directly in type annotations, which is very convenient, but sometimes a type will be used multiple times. At this time, we prefer to refer to it by a single name.

This is the type alias. The so-called type alias, as the name suggests, can refer to any type of name. The syntax of a type alias is:

type Point = {
  x: number;
  y: number;
};
 
// Exactly the same as the earlier example
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

You can use the type alias to give any type a name. For example, name a union type:

type ID = number | string;

Note that aliases are unique aliases. You cannot use type aliases to create different versions of the same type. When you use a type alias, it is the same as the type you write. In other words, the code may look illegal, but it is still legal for TypeScript because both types are aliases of the same type:

type UserInputSanitizedString = string;
 
function sanitizeInput(str: string): UserInputSanitizedString {
  return sanitize(str);
}
 
// Create a sanitized input
let userInput = sanitizeInput(getInput());
 
// Can still be re-assigned with a string though
userInput = "new input";

Interfaces

interface declaration is another way to name object types:

interface Point {
  x: number;
  y: number;
}
 
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

Just like the type alias we used in the previous section, this example can also run, just as we used an anonymous object type. TypeScript only cares about the structure of the value passed to printCoord -- whether the value has the expected attribute. It is this feature that only cares about the structure and capabilities of types that makes TypeScript a structured type system.

Differences between type aliases and interfaces

Type aliases are very similar to interfaces. Most of the time, you can choose to use them. Almost all features of the interface can be used in type. The key difference between the two is that the type alias itself cannot add new attributes, and the interface can be extended.

// Interface
// Extend types by inheritance
interface Animal {
  name: string
}

interface Bear extends Animal {
  honey: boolean
}

const bear = getBear() 
bear.name
bear.honey
        
// Type
// Extend type by intersection
type Animal = {
  name: string
}

type Bear = Animal & { 
  honey: boolean 
}

const bear = getBear();
bear.name;
bear.honey;
// Interface
// Add a new field to an existing interface
interface Window {
  title: string
}

interface Window {
  ts: TypeScriptAPI
}

const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});
        
// Type
// Cannot be changed after creation
type Window = {
  title: string
}

type Window = {
  ts: TypeScriptAPI
}

// Error: Duplicate identifier 'Window'.

You will learn more in the following chapters. Therefore, it doesn't matter if the following contents can't be understood immediately:

Most of the time, you can choose according to your personal preferences. TypeScript will tell you whether it needs to be declared in other ways. If you like exploratory use, use the interface until you need the type feature.

Type Assertions

Sometimes, you know the type of a value, but TypeScript doesn't.

For example, if you use document.getElementById, TypeScript only knows that it will return an HTMLElement, but you know that you want to get an htmlcanvas element.

In this case, you can use type assertion to specify it as a more specific type:

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

Like type annotations, type assertions are removed by the compiler and do not affect any runtime behavior.

You can also use angle bracket syntax (note that it cannot be used in. tsx files), which is equivalent:

const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");

Remember: because type assertions will be removed during compilation, there will be no type assertion check at runtime. Even if the type assertion is wrong, there will be no exception or null.

TypeScript only allows type assertions to be converted to a more specific or less specific type. This rule can prevent some impossible cast types, such as:

const x = "hello" as number;
// Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

Sometimes, this rule will appear very conservative and prevent you from effectively converting types. If this happens, you can use double assertions, first asserting as any (or unknown), and then asserting as the expected type:

const a = (expr as any) as T;

Literal Types

In addition to the common types string and number, we can also declare types as more specific numbers or strings.

As we all know, there are many ways to declare variables in JavaScript. For example, var and let, variables declared in this way can be modified later, and const, variables declared in this way can not be modified, which will affect TypeScript to create types for literal quantities.

let changingString = "Hello World";
changingString = "Olá Mundo";
// Because `changingString` can represent any possible string, that
// is how TypeScript describes it in the type system
changingString;
// let changingString: string
const constantString = "Hello World";
// Because `constantString` can only represent 1 possible string, it
// has a literal type representation
constantString;
// const constantString: "Hello World"

Literal types themselves are not very useful:

let x: "hello" = "hello";
// OK
x = "hello";
// ...
x = "howdy";
// Type '"howdy"' is not assignable to type '"hello"'.

It is much more useful to combine union types. For example, when a function can only pass in some fixed strings:

function printText(s: string, alignment: "left" | "right" | "center") {
  // ...
}
printText("Hello, world", "left");
printText("G'day, mate", "centre");
// Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'.

The numeric literal type is the same:

function compare(a: string, b: string): -1 | 0 | 1 {
  return a === b ? 0 : a > b ? 1 : -1;
}

Of course, you can also associate with non literal types:

interface Options {
  width: number;
}
function configure(x: Options | "auto") {
  // ...
}
configure({ width: 100 });
configure("auto");
configure("automatic");

// Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.

There is also a literal type, boolean literal. Because there are only two boolean literal types, true and false, the type boolean is actually an alias of the union type true | false.

Literal Inference

When you initialize a variable to an object, TypeScript will assume that the value of the object's attribute will be modified in the future. For example, if you write such code:

const obj = { counter: 0 };
if (someCondition) {
  obj.counter = 1;
}

TypeScript does not think that obj.counter was 0 before and is now assigned to 1 is an error. In other words, obj.counter must be of type string, but not necessarily 0, because the type can determine the reading and writing behavior.

The same applies to Strings:

declare function handleRequest(url: string, method: "GET" | "POST"): void;

const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);

// Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

In the above example, req.method is inferred as a string instead of "GET", because there may be other code between creating req and calling handleRequest function. Req.method may be assigned a new string, such as "Guess". So TypeScript reports an error.

There are two ways to solve this problem:

  1. Add a type assertion to change the inference result:
// Change 1:
const req = { url: "https://example.com", method: "GET" as "GET" };
// Change 2
handleRequest(req.url, req.method as "GET");

Modification 1 means "I intend to make the type of req.method literal type" GET ", which will prevent the possible assignment of fields such as" GUESS "in the future". Modification 2 means "I know the value of req.method is" GET "

  1. You can also use as const to convert the entire object to a type literal:
const req = { url: "https://example.com", method: "GET" } as const;
handleRequest(req.url, req.method);

The effect of as const is similar to const, but for the type system, it can ensure that all attributes are given a literal type instead of a more general type, such as string or number.

null and undefined

JavaScript has two primitive types of values, which are used to indicate vacancy or uninitialization. They are null and undefined respectively.

TypeScript has two corresponding types with the same name. Their behavior depends on whether they are turned on strictNullChecks Options.

strictNullChecks closed

When strictNullChecks When the option is turned off, if a value may be null or undefined, it can still be accessed correctly or assigned to any type of attribute. This is a bit similar to languages without null checking (such as C#, Java). The lack of these checks is the main source of bug s, so we always recommend developers to open them strictNullChecks Options.

strictNullChecks open

When strictNullChecks When the option is turned on, if a value may be null or undefined, you need to check these values before using its methods or properties, just like checking whether it is undefined before using optional properties. We can also use type narrowing to check whether the value is null:

function doSomething(x: string | null) {
  if (x === null) {
    // do nothing
  } else {
    console.log("Hello, " + x.toUpperCase());
  }
}

Non null assertion operator (suffix!)

TypeScript provides a special syntax to remove null and undefined from the type without any check. This is to write after any expression, This is a valid type assertion, indicating that its value cannot be null or undefined:

function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}

Like other type assertions, this does not change any runtime behavior. Important thing to say again, only use it when you clearly know that this value cannot be null or undefined.

Enumeration (Enums)

Enumeration is a new feature added by TypeScript to describe that a value may be one of multiple constants. Unlike most TypeScript features, this is not a type level increment, but will be added to the language and runtime. Because of this, you should understand this feature. But you can wait and use it unless you're sure you want to use it. You can Enumeration type Page for more information.

Less Common Primitives

Let's mention some of the primitive types that remain in JavaScript. But we won't go into depth.

bigInt

ES2020 introduces the primitive type BigInt, which is used to represent very large integers:

// Creating a bigint via the BigInt function
const oneHundred: bigint = BigInt(100);
 
// Creating a BigInt via the literal syntax
const anotherHundred: bigint = 100n;

You can learn more in.

symbol

This is also an original type in JavaScript. Through the function Symbol(), we can create a globally unique reference:

const firstName = Symbol("name");
const secondName = Symbol("name");
 
if (firstName === secondName) {
  // This condition will always return 'false' since the types 'typeof firstName' and 'typeof secondName' have no overlap.
  // Can't ever happen
}

You can Symbol page Learn more.

TypeScript series

  1. Introduction to the basics of TypeScript
  2. Common types of TypeScript (I)
  3. TypeScript type narrowing
  4. Functions of TypeScript
  5. Object type of TypeScript
  6. Generics of TypeScript
  7. Keyof operator of TypeScript
  8. Typeof operator of TypeScript
  9. Index access type of TypeScript
  10. Condition type of TypeScript

Wechat: "mqyqingfeng", add me to Yu Yu's only reader group.

If there is any mistake or lack of preciseness, please be sure to correct it. Thank you very much. If you like it or have some inspiration, welcome star, which is also an encouragement to the author.

Tags: Javascript Front-end html5 TypeScript

Posted on Thu, 02 Dec 2021 13:24:26 -0500 by me102