This is the first day of my participation in the Gwen Challenge in November. Check out the details: the last Gwen Challenge in 2021

One of TypeScript’s core tenets is type checking for the _ structure _ that values have

In TypeScript, interfaces name these types and define contracts for your code or third-party code

The basic use

Interfaces are used to define complex data type destructions such as objects

TS only cares about the shape of the value. As long as the incoming object meets the requirements mentioned in the interface, it is allowed

// Define an interface. You are advised to use I to start with an uppercase letter
interface Iuser {
  firstname: string.lastname: string
}

// Do not use interfaces
// function getFullName({ firstname, lastname }: { firstname: string, lastname: string }) {
// return `${firstname} - ${lastname}`
// }

// Use the interface
function getFullName({ firstname, lastname }: Iuser) {
  return `${firstname} - ${lastname}`
}

getFullName({
  firstname: 'Klaus'.lastname: 'Wang'
})
Copy the code

Optional parameters

Not all attributes in the interface are required. Some exist only under certain conditions, or not at all

An interface with optional attributes is similar to a normal interface definition, except that the optional attribute name definition is followed by one. symbol

The advantage of optional attributes is that possible attributes can be predefined

interface Iuser {
  firstname: string, lastname? :string // This parameter is optional
}

function getFullName({ firstname, lastname = 'Wang' }: Iuser) {
  return `${firstname} - ${lastname}`
}


console.log(getFullName({
  firstname: 'Klaus'
}))
Copy the code

Read-only property

Some object properties can only change their value when the object is created. You can specify read-only properties with readonly before the property name

interface Iuser {
  readonly firstname: string.// Read-only attributelastname? :string
}

Construct an Iuser by assigning an object literal
// After assignment, the value of firstName is no longer allowed to change
const user: Iuser = {
  firstname: 'Klaus'
}

// user.firstname = 'Alex' // error
Copy the code
interface Iarr {
  0: string.readonly 1: number
}

const arr: Iarr = ['Klaus'.23]

// arr[1] = 24 // error
Copy the code

Defining the function structure

Interfaces can describe the various shapes that objects in JavaScript can have. In addition to describing ordinary objects with attributes, interfaces can also describe function types.

interface IFoo {
  // This is actually a call signature - it looks like a function definition with only the argument list and return value types
  // Each parameter in the parameter list needs a name and type
  // The parameter name of the function does not need to match the name defined in the interface
  // TS will check the parameters of the function one by one, and only require that the parameter types in the corresponding positions are compatible
  (num1: number.num2: number) :number
}

// In the following example, num1 and num2 can be omitted because TS is derived from the interface type IFoo
const foo: IFoo = (num1: number, num2: number) = > num1 + num2
Copy the code

The index sign

The so-called index signature actually defines the type of the index, such as the type of the key in the object or the type of the index in the array

Types with index signatures are called indexable types

interface IFoo {
  // If an object is of type IFoo
  // All keys in this object must be strings
  // The value must be of numeric type
  [key: string] :number
}

const foo: IFoo = {
  // name: 'Klaus' ---> error
  age: 23
}
Copy the code

If there are specific attributes, the type of the index signature value must be compatible with the type of the specific key

// Attribute "name" of type "string" cannot be assigned to index "number" of type "string"
interface IFoo {
  name: string.// ---> error
  age: number,
  [key: string] :number
}
Copy the code

You can modify it as follows

interface IFoo {
  name: string.age: number,
  [key: string] :number | string
}
Copy the code

Typescript supports two index signatures: strings and numbers. You can use both types of indexes

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

class Animal {
  name: string;
}
class Dog extends Animal {
  breed: string;
}

// error: Dog is not a subtype of Animal for a string index
interface NotOkay {
  [x: number]: Animal;
  [y: string]: Dog;
}
Copy the code

Interface inheritance

Interfaces can also inherit from each other. This allows us to copy members from one interface to another, giving us more flexibility to split the interface into reusable modules

interface IAnimal {
  name: string
  age: number
}

interface IPerson extends IAnimal {
  address: string
}

const person: IPerson = {
  name: 'Klaus'.age: 23.address: 'shanghai'  
}
Copy the code

An interface can inherit multiple interfaces to create a composite interface of multiple interfaces

interface IName {
  name: string
}

interface IAge {
  age: number
}

interface IPerson extends IName, IAge {
  address: string
}

const person: IPerson = {
  name: 'klaus'.age: 23.address: 'shanghai'
}
Copy the code

Interfaces and classes

Like C# or Java interfaces, TypeScript can use them to explicitly force a class to conform to a contract

interface IPerson {
  name: string
  printName(name: string) :void
}

class Person implements IPerson {
  name = 'Klaus'

  printName(name: string) {
    console.log(name)
  }
}
Copy the code

Mixed-type interface

In JS, a function is a special kind of object, that is, a function can either be executed or add attributes. Again, this is supported in TS

interface ICounter {
  count: number, () :void
}

function getCounter() :ICounter {
  // increment is a function, but it is also an object with the count attribute on it
  const increment = () = > increment.count++
  increment.count = 0
  return increment
}

const counter: ICounter = getCounter()

counter()
console.log(counter.count)
Copy the code

Redundant attribute check

The object arguments we pass in actually contain a lot of attributes, and we want the compiler to check only if the required attributes exist and their types match.

However, the interface will report an error if there are fewer or more attributes during the detection. At this time, we need to bypass the REDUNDANT attribute detection of TS

interfaceSquareConfig { color? :string; width? :number;
}

function createSquare(config: SquareConfig) :{ color: string; area: number } {
  // ...
}

// Here the colour property is actually wrong - it's a bug
But for the SquareConfig interface, both the color and width attributes are optional
// The compiler only checks if the required attributes exist and their types match
// The bug is ignored
let mySquare = createSquare({ colour: "red".width: 100 });
Copy the code

In TypeScript, object literals are treated specially and are checked for extra attributes when they are assigned to variables or passed as arguments. You get an error if an object literal has any properties that the “target type” does not contain (there are extra properties).

Sometimes, however, we know that there is not a bug in the code, but that the object parameters passed in contain extra attributes. In this case, we need to bypass TS’s extra attribute check

Method 1: Type assertion

interface IUser {
  firstname: string
  lastname: string
}

function printName(user: IUser) {
  console.log(`${user.firstname} - ${user.lastname}`)
}

printName({
  firstname: 'Klaus'.lastname: 'Wang'.age: 23
} as IUser)
Copy the code

Method 2: Index signature

// IUser must have firstName and lastName
// And any other attribute values whose key is string
interface IUser {
  firstname: string
  lastname: string
  [key: string] :any
}

function printName(user: IUser) {
  console.log(`${user.firstname} - ${user.lastname}`)
}

printName({
  firstname: 'Klaus'.lastname: 'Wang'.age: 23
})
Copy the code

Method 3: Attribute erasure

interface IUser {
  firstname: string
  lastname: string
}

function printName(user: IUser) {
  console.log(`${user.firstname} - ${user.lastname}`)}const user = {
  firstname: 'Klaus'.lastname: 'Wang'.age: 23
}

// Redundant type detection is essentially for object literals, not variables
// The firstName and lastName attributes that must exist on IUser are sufficient
// Superfluous attributes are ignored
printName(user)
Copy the code