This is the eighth day of my participation in Gwen Challenge

Interfaces: Used to constrain the structure and types of objects, functions, and classes. Is a contract for code collaboration

Example 1: Suppose you get the data from the back end and render it to the interface

Scenario 1: The backend sometimes returns redundant fields

interface List {
  id: number;
  name: string;
}

interface Result {
  data: List[];
}

function render(res: Result) {
  res.data.forEach((value) = > {
    console.log(value.id, value.name);
  });
}

let result = {
  data: [{id: 1.name: "A" },
    { id: 2.name: "B" },
    // Passing extra fields such as age does not generate an error. Principle: "Duck type identification method"
    { id: 3.name: "C".age: 20},]}; render(result);Copy the code

The backend sometimes returns redundant fields, but TS does not report an error, because TS uses “duck identification”.

Explanation:

For example, if a bird swims like a duck, walks like a duck, and quacks like a duck, then duck TS is allowed as long as the parameters passed meet the necessary conditions, even if the passed extra fields can pass the type check

Scenario 2: The function passes the object literal directly

interface List {
  id: number;
  name: string;
}

interface Result {
  data: List[];
}

function render(res: Result) {
  res.data.forEach((value) = > {
    console.log(value.id, value.name);
  });
}

// Note that the function is assigned directly
render({
  data: [{id: 1.name: "A" },
    { id: 2.name: "B" },
    // age generates an error
    { id: 3.name: "C".age: 20},]});Copy the code

Solution one: variables as function parameters

Just like scene one. Assign an object literal to a variable, and then, in the function, take that variable as an argument

Solution 2Types of assertions

Explicitly tell the compiler what type of argument is passed in

render({
  data: [{id: 1.name: "A" },
    { id: 2.name: "B" },
    // age generates an error
    { id: 3.name: "C".age: 20},],}as Result);
Copy the code

Solution 3String index signature

interface List {
  id: number;
  name: String;
  // Index the List with any string to get any result
  [x: string] :any;
}

interface Result {
  data: List[];
}

function render(res: Result) {
  res.data.forEach((value) = > {
    console.log(value.id, value.name);
  });
}

render({
  data: [{id: 1.name: "A" },
    { id: 2.name: "B" },
    { id: 3.name: "C".age: 20},]});Copy the code

Scenario 3: Check whether a field exists in the data

interface List {
  id: number;
  name: String;
  // Add a question mark to indicate that the age field can be absent or presentage? :number;
}
interface Result {
  data: List[];
}
function render(res: Result) {
  res.data.forEach((value) = > {
    console.log(value.id, value.name);
    // If the age field is used, it must be checked
    if (value.age) {
      console.log(value.age); }}); }let result = {
  data: [{id: 1.name: "A" },
    { id: 2.name: "B" },
    { id: 3.name: "C".age: 20},]}; render(result);Copy the code

Scenario 4: Fields are read-only

Readonly: can only be read but cannot be modified

interface List {
  // Can only read, cannot modify otherwise will report an error
  readonly id: number;
  name: String; age? :number;
}
interface Result {
  data: List[];
}
function render(res: Result) {
  res.data.forEach((value) = > {
    console.log(value.id, value.name);
    // There will be an error
    value.id = 1;
  });
}
let result = {
  data: [{id: 1.name: "A" },
    { id: 2.name: "B" },
    { id: 3.name: "C".age: 20},]}; render(result);Copy the code

Scenario 5: Indexable interface

When the number of data fields is uncertain, you can use indexable interfaces, which can be strings or numbers

An indexable type interface represented by a number

interface StringArray {
  /* Indexing StringArray with any number yields a string */
  [index: number] :string;
}

// Use scenario 1: string arrays
let chars: StringArray = ["a"."b"];

// Use scenario 2: use numbers as keys and strings as worth objects
let a: StringArray = { 1: "a".2: "Digital" };
Copy the code

In the example above, we define the StringArray interface, which has index signatures. The index signature indicates that when StringArray is indexed by number, it returns a value of type String.

An indexable type interface represented as a string

interface Names {
  /* Indexing Names with any string gives a string equivalent to a string object */
  [x: string] :string;
  // You cannot declare a member of type number, otherwise an error will be reported
  // y:number
}

let myObject: Names = { name: "Xiao Ming".className: "Class 1, Grade 1" };
Copy the code

The two can be mixed

Ohters can be indexed either by numbers or by strings
interface Ohters {
  [x: string] :string;
  [y: number] :string;
}

let myObject: Ohters = {
  1: "Name".a: "a".name: "a".2: "Digital"};Copy the code

The return value of the digital index signature has to be a subtype of the return value of the string index signature because JavaScript does a type conversion, converts number to string. This ensures type compatibility

TypeScript supports two index signatures: strings and numbers. Both types of indexes can be used, but the return value of the numeric index must be a subtype of the return value type of the string index. This is because when you index a number, JavaScript converts it to a string and then indexes the object. That is, indexing with 100 (a number) is the same as indexing with 100 (a string), so the two need to be consistent.

// This is incompatible with string
interface Ohters1 {
  [x: string] :string;
  // There will be an error
  [y: number] :number;
}

// Solution 1
interface Ohters2 {
  [x: string] :any;
  [y: number] :number;
}
// Solution 2
interface Ohters3 {
  [x: string] :number;
  [y: number] :number;
}
Copy the code

Use variables to define function types

let myAdd: (x: number, y: number) = > number = function (
  x: number,
  y: number.) :number {
  return x + y;
};

/ / 12
myAdd0(2.10);
Copy the code

The function type consists of two parts: the parameter type and the return value type. Both are needed when writing full function types

Use interfaces to define function types

interface Add {
  (x: number.y: number) :number;
}

let myAdd: Add = (a, b) = > a + b;

/ / 80
myAdd(30.50);
Copy the code

TypeScript’s type system extrapolates parameter types because functions are assigned directly to Add type variables. The return value type of a function is inferred from its return value (in this case, number). If we tell this function to return another type, the type checker will warn us that the return value type of the function does not match the definition in the Add interface.

Use type aliases to define function types

type Add1 = (x: number, y: number) = > number;

let myAdd1: Add1 = (a, b) = > a + b;

/ / 3
myAdd1(1.2);
Copy the code

4. Mixed type interfaces

This interface can either define functions or, like an object, have properties and methods

interface Lib {
  (): void;
  version: string;
  doSomeThing(): void;
}

function getLib() {
  let lib: Lib = (() = > {}) as Lib;
  lib.version = "1.0";
  lib.doSomeThing = () = > {};
  return lib;
}

let lib1 = getLib();

lib1();

lib1.doSomeThing();

let lib2 = getLib();

lib2();

lib2.doSomeThing();
Copy the code

conclusion

Several ways to define functions

A: the function

The type of the return value can be omitted by the type inference of TS

function test(x: number, y: number) {
  return x + y;
}

test(12.15);
Copy the code

Method 2: Use variables to define function types

let myAdd0: (x: number, y: number) = > number = function (
  x: number,
  y: number.) :number {
  return x + y;
};

myAdd0(2.10);
Copy the code

Method 3: Define a function type through a type alias

type Add1 = (x: number, y: number) = > number;

let myAdd1: Add1 = (a, b) = > a + b;

myAdd1(1.2);
Copy the code

Method 4: Define function types through interfaces

interface Add {
  (x: number.y: number) :number;
}

let myAdd: Add = (a, b) = > a + b;

myAdd(30.50);
Copy the code

The last three only define the type of the function and have no real implementation, so when called, to write the function body in TS parameters and arguments must correspond one to one

An optional argument in a function

function myAdd(x: number, z: number, y? :number) {
  if (y) {
    return x + y + z;
  } else return x + z;
}

myAdd(1.2);
Copy the code

Optional parameters must come after required parameters

Default values for arguments in a function

function myAdd1(x: number, z = 10, y? :number) {
  if (y) {
    return x + y + z;
  } else return x + z;
}
/ / 30
myAdd1(20);

function myAdd2(x: number, z = 10, a: number) {
  return x + z + a;
}
Copy the code
function myAdd3(x: number, y = 0, z: number, q = 1) {
  return x + y + z + q;
}

// An argument for 'y' was not provided
myAdd3(1);
Copy the code

The default parameter cannot be omitted before it is required. Undefined must be explicitly passed to obtain the default value

function myAdd3(x: number, y = 0, z: number, q = 1) {
  return x + y + z + q;
}

/ / 5
console.log(myAdd3(1.undefined.3));
Copy the code

The type of the remaining arguments in the function

function myAdd3(x: number. rest:number[]) {
  return x + rest.reduce((pre, cur) = > pre + cur);
}
/ / 16
myAdd3(10.1.2.3);
Copy the code

_ function overload, _

Function: There is no need to give different function names for functions with similar functions.

JavaScript itself is a dynamic language. It is common for JavaScript functions to return different types of data depending on the parameters they pass in. The same function provides multiple function type definitions for function overloading. The compiler handles function calls based on this list

In order for the compiler to choose the right type of check, it is similar to the processing flow in JavaScript. It looks up the overload list and tries to use the first overload definition. Use this if it matches. Therefore, when defining overloads, always put the most precise definition first.

function add6(. rest:number[]) :number;

function add6(. rest:string[]) :string;

// Implement overloading in a function of a broader type
function add6(. rest:any) :any {
  let first = rest[0];
  if (typeof first === "string") {
    return rest.join("|");
  }
  if (typeof first === "number") {
    return rest.reduce((pre: number, cur: number) = >pre + cur); }}/ / 6
add6(1.2.3);
// a|b|c
console.log(add6("a"."b"."c"));
Copy the code