Today let’s talk about those object types in TS —-TypeScript series: object types

Objects are the basic way we organize data. In addition to the general object types, TS provides a body of knowledge on read-only arrays, tuples, attribute modifiers, index signatures, and more. In the previous sections, we looked at the basic use of objects. A common object type can be specified with literals, interfaces, and type aliases. Now it’s time to look at common object property modifiers, type extension, index signatures, and built-in special object types. Don’t have a date yet? Then get a new one!

[toc]

A,Attribute modifier

Using attribute modifiers, we can enhance the constraints of an object’s attributes.

1. ? Optional attribute

Optional attributes we’ve seen before. When defining an object type, place the question mark “?” after the attribute name. , indicating that the attribute is optional. Thereafter, objects of that type may or may not have this property. Therefore, the property can be undefined in addition to the type we constrain it to, which can cause some problems. As mentioned in the previous article, this can be solved by using truth checking for type reduction, or by setting default values for function parameters.

// Gender is an optional attribute
interface Person {
  name: string.age: number, gender? :1 | 2
}
// Can have all attributes
const cc: Person = {
  name: "cc".age: 18.gender: 2
}

// Optional attributes can be missing
const yy: Person = {
  name: "yy".age: 18
}
Copy the code

2. readonly Read-only property

You can set one or some properties of an object to read-only by prefixing them with the keyword readonly. The read-only properties of an object are generally not allowed to be reassigned in TS. Read-only properties do not mean that they cannot be modified at all. If the read-only property is an object type, we can modify its members.

// Id is read-only and cannot be reassigned
interface Person {
  name: string.age: number.readonly idInfo: {
    id: string.addr: string}}// Can have all attributes
const cc: Person = {
  name: "cc".age: 18.idInfo: {
    id: "42xxxx199x04xxxx1X".addr: "Wuhan"}}The cc id is set to read-only and cannot be reset
cc.idInfo = {id: "xiao cai ji".addr: "beijing"}
// This is the way to do it
cc.idInfo.addr = "beijing"
Copy the code

However, in TS we still have methods to reassign read-only properties. Due to the type checking mechanism of TS, when checking the compatibility of two object types, only the type is checked, and the property is not checked for readOnly. For example, the following two object types are completely compatible with each other.

// Id is read-only and cannot be reassigned
interface Person {
  readonly name: string.readonly age: number.readonly idInfo: {
    id: string.addr: string}}// Without the readonly attribute, all members can be assigned
interface People {
  name: string.age: number.idInfo: {
    id: string.addr: string}}// Note that the Person attribute is read-only
let person: Person = {
  name: 'gg'.age: 15.idInfo: {
    id: "qaq".addr: "zzZ"}}// Declare an object of type person whose properties are not read-only
let people: People

// Type compatible, person with a read-only attribute can be assigned to people
// People is of type people, and its attributes can be modified; Person is a Person type whose properties are read-only
// For those familiar with JS reference types, people and person are the same object
people = person

// Change the people attribute, and we see that person is also changed, because they are actually the same object
people.name = 'pp'
people.age = 20
Copy the code

As you can see from this chestnut, we can override the read-only property of another object by an object that does not have the corresponding read-only property and whose property type is compatible. Therefore, it is important to be aware of this situation when using read-only attributes to avoid unexpected complications.

Second,The index sign

How much do you know about function signatures, function overloading, and generic functions in the previous TS article? In the Typescript function series, we learned about function signatures. It’s more or less like the index signature here.

When we define an object type using interface or type alias, we used to list all the attribute names and specify the types of their values. But sometimes, we don’t know what attributes are in the object and can’t list them individually. At this point, we can use index signatures to specify the type of the attribute name and the corresponding attribute value.

// With index signatures, we can define a pseudo-array
// Specifies that the attribute name must be number
interface StringArray {
  [index: number] :string;
}
// Distinguish it from the real array
let strArr1: StringArray = {
  0: "100".5: "95"
}
Error reported because the attribute name grades was not of type number and did not comply with the StringArray constraint
let strArr2: StringArray = {
  grades: "100",}Copy the code

When we use the number type as the attribute name, JS converts it to string before putting it into the object. Index signatures can be mixed with specified attribute names and attribute modifiers, in which case the object must contain listed attributes (except optional attributes) and extend attributes that conform to the constraints of the index signature.

interface Person {
  [x:string] :string.100: string.50? :string
}

let cc: Person = {
  100: "Happy 100".6: "I'm a property extended by the index signature."
}

let yy: Person = {
  0: "QAQ".100: "555~".50: "0.0"
}
Copy the code

There can be more than one set of index signatures, but the return values of each index signature should be compatible with each other.

interface Person {
  [x: string] :string,
  [y: number] :string,}Copy the code

You can add the readonly keyword to the signature of the index signature to make the signed attribute read-only.

interface Person {
  readonly [x:number] :string | number
}

let cc: Person = {
  2: "cc".5: 18.10: "boi"
}
Copy the code

Three,Type expansion

From the configuration of an existing object type, we can generate a new type that not only contains all the properties of the original type, but also has its own unique properties. In this way, it is convenient to realize type reuse, avoid too much repeated code knocking, and improve our work efficiency. When defining a new type, the interface and type keywords are used, and type extensions are implemented differently.

1. Type inheritance

Object types declared using the interface keyword can be extended from other object types using the extends keyword to get all the property configurations and property signatures of the parent type without having to list them all over again. Multiple object types can be inherited simultaneously.

type A = {
  name: string
}

interface B {
  age: number
}

// The C type inherits the A type, including the name attribute, without adding other attributes
interface C extends A {}

// The CC type inherits A and B types, including name, age, and gender attributes listed by itself
interface CC extends A, B {
  gender: 1 | 2
}
Copy the code

2. Type crossover

When an object type is aliased by the type keyword, the ampersand is used to concatenate multiple types, resulting in a new type that contains the properties of all other object types, known as type crossing.

type A = {
  name: string
}

interface B {
  age: number
}
// Type CC contains the name and age attributes
type CC = A & B
Copy the code

It is worth noting that if the & is concatenated to a simple union type, the resulting new type is the public type of the type on both sides of the ampersand.

type A = string | number
type B = string[] | number
// C is the number type of A and B
type C = A & B
Copy the code

This and the use of ampersand between object types looked completely different, and I was a little confused at first. But we can think about it essentially, and there’s no more confusion. In a type alias defined by the keyword type, the ampersand concatenates an existing type to produce a new type that satisfies the type constraints on both sides of the ampersand.

Therefore, in the chestnut of object types, the new type CC needs to satisfy both the constraint of type A (so CC must have all the attributes of A) and the constraint of type B (so it must have all the attributes of type B), that is, CC has all the attributes of A and B.

In the chestnut of A simple union type, the new type C satisfies both the type constraint of A (either number or string) and the type constraint of B (either string array string[] or number), so the resulting C is number.

Four,The generic object

Generics: Replace abstract, unknown types with concrete, known types by declaring type parameters (which can have more than one) to represent temporarily unknown types using Angle brackets (<>), passing in the corresponding type (or by TS automatic inference) to replace the corresponding occurrence of type parameters when actually declaring variables. A type parameter refers to a type, such as

each refers to a type that is temporarily unknown. Use generics to define object types, and you get a generic object.
,k,u…>

// Type T represents a type that is temporarily unknown
interface PersonInfo<T> {
  info: T
}
// Pass in the type variable string, in which case string replaces T in its place
let p1: PersonInfo<string> = {
  info: 'cc'
}

let p2: PersonInfo<number> = {
  info: 18
}

let p3: PersonInfo<'male' | 'woman'> = {
  info: "Male"
}
Copy the code

Generic objects are commonly used in generic functions. See TS for function signatures, function overloading, and generic functions. Typescript series :(2) functions.

Generics can also be used in type aliases. In addition to defining object types, type aliases can also define a variety of other types using generics. As a result, we can use generic nesting to define more complex type structures. This article focuses on object types, but more on generics later. Here is a chestnut from the official website.

type OrNull<Type> = Type | null;

type OneOrMany<Type> = Type | Type[];

type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;

type OneOrManyOrNullStrings = OneOrManyOrNull<string>;
Copy the code

Array types

As we know, an array is a special type of object. (number[], string[]) is a shorthand for Array<number>, Array<string>. Modern JS also provides other new types of generic structures, such as Map<T, K>, Set<T>, Promise<T>, etc. The number of type parameters depends on the behavior of each type.

1. ReadonlyArray Read-only array

A read-only array is a special array provided by TS in which no member can be modified.

  • Add, delete, replace and other operations cannot be carried out, and push, POP and other methods to modify themselves cannot be used.

  • You can use array methods that do not modify themselves. The slice method, for example, returns a plain array

  • Represents a type that cannot be used as a constructor. The new operator cannot be used.

  • When declaring a variable of a read-only array type, you need to specify a type parameter. Read-only arrays can only hold values of that type.

  • A variable of read-only array type can itself receive an assignment from a normal array.

  • Ordinary arrays cannot accept assignments from read-only arrays

// Specify the type parameter number, that is, the number in the read-only array
let a: ReadonlyArray<number> = [123]
/ / error
a.pop()
// The slice method can be used
let cc = a.slice(0)

// This variable can be reassigned using normal
a = [1.2.3.4]

/ / error
let val = new ReadonlyArray()

let aa:Array<number> = [456]
/ / error
aa = a
Copy the code

2. Tuple Typestuples

A tuple type is another special array type that can generally store different types of members, limiting the length of the array and the type of each member. Ordinary arrays can also hold members of different types by specifying type parameters for union types. Tuple members can be modified by calling a series of array methods.

type UserInfo = [string.number.1 | 2 ]
Copy the code

Here the UserInfo is a tuple type, provisions of tuples in the one and only three members, members of the index of 0 is a string type, the index of 1 member is number type, the index for 2 members are literal joint type 1 | 2.

  • Tuple types can be deconstructed.
function introduceUser(info:UserInfo) :string{
  const [name, age, gender] = info
  return I was `${name}This year,${age}` age?
}
Copy the code
  • Tuples can also specify optional members:
// The third member is optional
type UserInfo = [string.number, (1 | 2)? ]
Copy the code
  • Remaining arguments can be used in tuples to specify the types of members at one or some index locations and the types of other members. At this point, the tuple has no length limit.
type FamilyMember = string[]
// Specify the type at index 0 as string, the type at index 1 as number, and the remaining type as FamilyMember
type UserInfo1 = [string.number. FamilyMember[] ]/ / in the same way
type UserInfo2 = [string.number. FamilyMember[],boolean]
type UserInfo3 = [...FamilyMember[], string.number]
Copy the code
  • Readonly Indicates the type of the read-only tuple

    We can declare a read-only tuple using a shorthand similar to the array type: readonly [string, number], making the tuple’s members read-only and unmodifiable. The readonly keyword can only be used before a literal array or literal tuple type, not before a type alias.

    // ok
    let cc: readonly [string.number] = ['cc'.18]
    / / an error
    let yy: readonly UserInfo3 = [['dd'].'1'.1]
    // before literal tuples, ok
    let yy: readonly [...FamilyMember[], string.number] = [['dd'].'1'.1]
    
    // Error, read-only attribute cannot be modified
    cc[0] = 'yy'
    Copy the code

    In addition, if we use a constant assertion on an array: as const, the type of the array becomes a read-only tuple.

    let yy = ['yy'.18] as const
    
    // Error, read-only attribute cannot be modified
    yy[0] = 'cc'
    Copy the code
  • ReadonlyArray, tuple, and readonly tuple are different.

    • ReadonlyArray read-only array: Members of an array cannot be modified. They cannot be added, deleted, or modified. Each member cannot be assigned a separate type and can be reassigned.

    • Tuple: Generally specify the length and the type of each member. There is no limit on the length when using the residual parameter method to declare the tuple type. A tuple can modify the value of a member; Can be reassigned;

    • Readonly tuple: specifies the length and the type of each member. When using the remaining parameter method to declare the tuple type, there is no limit on the length. The value of the member cannot be modified or reassigned.

So much for object types. If you find anything wrong with my post, please point it out in the comments section. Next time we’ll talk about generics. Be there or be square.