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