Sometimes we want an interface to allow arbitrary attributes. TypeScript gives us two index signatures: strings and numbers.

Such as:

interface Person{
    name: string;
    age: number;
    [propName: string]: any;
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
};
Copy the code

Use [propName: string] to define any attribute that takes a string value

Such as:

interface IPerson {
    name: string; age? :number;
    [propName: string] :string;
}

let tom: IPerson = {
    name: 'Tom'.age: 25.gender: 'male'
};

// index.ts(3,5): error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'.
/ / index. Ts (7, 5) : error TS2322: Type '{[x: string] : string | number; name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Index signatures are incompatible.
// Type 'string | number' is not assignable to type 'string'.
// Type 'number' is not assignable to type 'string'.
Copy the code

Take note:

1. Once any attribute is defined and any attribute is of type String, the type of both the determined attribute and the optional attribute must be a subset of its type

👆🌰 The value of any attribute can be string, but the value of the optional attribute age is number. Number is not a child of string, so an error is reported. The {name: “Tom”, the age: 25, gender: ‘male’} type inference into {[x: string] : string | number; name: string; age: number; gender: string; }

2. An interface can define only one attribute of the same type. If an interface has arbitrary attributes of multiple types, you can use the union type in any attribute

So it can be rewritten as:

interface IPerson {
    name: string; age? :number;
    [propName: string] :string | number;
}

let tom: IPerson = {
    name: 'Tom'.age: 25.gender: 'male'
};
Copy the code

but… Note that 👇🌰 is not an error:

interface IArgument {
  [index: number] :number;
  a: number;
  b: string;
  c: Function;
}
// This is ok?
Copy the code

3. Arbitrary attribute signatures of type number do not affect string

vs

Error: 👇🌰

interface IArgument {
  [index: string] :number;
  a: number;
  b: Function;
}
// This is an error.
Copy the code

Since b: Function is not a subset of the value type (number) of any property, we return to the constraint of point 1

See the difference?

Can we use both types of indexes? can

Both types of indexes can be used, but the value type of the numeric index must be a subtype of the value type of the string index. When you index a number, JavaScript converts it to a string and then indexes the object. That is, deindexing by 1 is the same as deindexing by “1”, so they need to be the same.

🌰 :

interface ITest{
	[prop: string]: object;
    [index: number]: Function;
}
Copy the code

and

interface ITest{
	[prop: string]: string;
    [index: number]: Function;
}
// Numeric index type 'Function' is not assignable to string index type 'string'.
Copy the code

Therefore:

When two arbitrary signatures coexist, the value type specified by the signature of type number must be a subset of the value type specified by the signature of type string

Once an arbitrary attribute ‘indexed as string type signature’ is defined, the type of the other attributes (deterministic, optional, read-only, and so on) must be a subset of its’ value type ‘.