How do I explicitly set properties on window objects

Used for JavaScript developers, for window. MyNamespace = window. MyNamespace | | {}; This line of code is not unfamiliar. To avoid conflicts during development, we usually have separate namespaces for certain functions.

. However, in the TS for window MyNamespace = window. MyNamespace | | {}; For this line of code, the TS compiler prompts the following exception:

Property 'MyNamespace' does not exist on type 'Window & typeof globalThis'.(2339)

The MyNamespace attribute does not exist on the Window & Typeof globalThis crossover type. So how to solve this problem? The simplest way to do this is to use type assertions:

(window as any).MyNamespace = {};

While using any can solve this problem, a better way to solve this problem is to extend the Window interface in the lib.dom.d.ts file as follows:

declare interface Window {
  MyNamespace: any;
}

window.MyNamespace = window.MyNamespace || {};
Copy the code

How to dynamically assign attributes to objects

In JavaScript, we can easily assign attributes to objects dynamically, such as:

let developer = {};
developer.name = "semlinker";
Copy the code

The above code works fine in JavaScript, but in TypeScript, the compiler prompts the following exception:

Property 'name' does not exist on type '{}'. (2339)
Copy the code

The {} type represents an object that contains no members, so it does not contain the name attribute. To solve this problem, we can declare a LooseObject type:

interface LooseObject {
  [key: string] :any
}
Copy the code

The LooseObject type can accept a key as a string and a value as an any field. With the LooseObject type in place, we can solve this problem by:

interface LooseObject {
  [key: string] :any
}

let developer: LooseObject = {};
developer.name = "semlinker";
Copy the code

The LooseObject type is very loose. In some application scenarios, we want to be able to declare mandatory and optional attributes in addition to supporting dynamic attributes.

For example, for a Developer interface that represents a Developer, we want the name attribute to be mandatory, the age attribute to be optional, and the string attribute to be set dynamically. For this requirement we can do the following:

interface Developer {
  name: string; age? :number;
  [key: string] :any
}

let developer: Developer = { name: "semlinker" };
developer.age = 30;
developer.city = "XiaMen";

Copy the code

In addition to using index signatures, we can also use TypeScript’s built-in tool type Record to define the Developer interface:

// type Record = { [P in K]: T; }
interface Developer extends Record<string, any> {
  name: string; age? :number;
}

let developer: Developer = { name: "semlinker" };
developer.age = 30;
developer.city = "XiaMen";
Copy the code

How to understand function overloading

3.1 Cute and hateful union types

Since JavaScript is a dynamic language, we usually call the same function with different types of arguments, and the function returns different types of results depending on the arguments:

function add(x, y) {
  return x + y;
}

add(1.2); / / 3
add("1"."2"); / / "12"
Copy the code

Since TypeScript is a superset of JavaScript, the above code can be used directly in TypeScript. However, when the TypeScript compiler turns on the noImplicitAny configuration item, this code prompts the following error message:

Parameter 'x' implicitly has an 'any' type.
Parameter 'y' implicitly has an 'any' type.
Copy the code

This information tells us that arguments x and y implicitly have type any. To solve this problem, we can set a type for the parameter. Because we want to add the function at the same time support the string and number types, so we can define a string | number joint type, at the same time we take individual name for the joint type:

type Combinable = string | number;
Copy the code

After defining the Combinable union type, let’s update the add function:

function add(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}
Copy the code

When you explicitly type the parameters of the add function, the error message disappears. Is the add function perfect? Let’s test it out

const result = add('semlinker'.' kakuqo');
result.split(' ');
Copy the code

In the above code, we call add with the strings ‘semlinker’ and ‘Kakuqo’ and save the result to a variable named result, which we assume is of type string. So we can call the split method on the string object normally. The TypeScript compiler now displays the following error message:

Property 'split' does not exist on type 'Combinable'.
Property 'split' does not exist on type 'number'.
Copy the code

It is clear that split properties do not exist on Combinable and number objects. Here comes the problem. How to solve it? At this point we can take advantage of TypeScript’s function overloading.

3.2 Function Overloading

Function overloading or method overloading is the ability to create multiple methods with the same name and different numbers or types of arguments.

function add(a: number, b: number) :number;
function add(a: string, b: string) :string;
function add(a: string, b: number) :string;
function add(a: number, b: string) :string;
function add(a: Combinable, b: Combinable) {
  // type Combinable = string | number;
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}
Copy the code

In the above code, we provide multiple function type definitions for the Add function to enable function overloading. In TypeScript, we can override member methods in a class in addition to normal functions.

Method overloading refers to a technique in which a method with the same name and different parameters (different parameter types, different number of parameters, or different sequence of parameters with the same number of parameters) is called based on the form of arguments and the method matching the operation is selected. So member methods in a class can be overloaded only if they have the same method name and different argument lists in the same class. Here’s an example of an overloaded member method:

class Calculator {
  add(a: number.b: number) :number;
  add(a: string.b: string) :string;
  add(a: string.b: number) :string;
  add(a: number.b: string) :string;
  add(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
    returna + b; }}const calculator = new Calculator();
const result = calculator.add('Semlinker'.' Kakuqo');
Copy the code

It’s important to note here that when the TypeScript compiler handles function overloads, it looks up the list of overloads and tries to use the first overloaded definition. Use this if it matches. Therefore, when defining overloads, always put the most precise definition first. Also, in the Calculator class, add(a: Combinable, b: Combinable){} is not part of the overload list, so we define only four overloaded methods for the Add member methods.

Welcome to discuss some problems encountered in ts learning process ~~