TypeScript细碎知识点:TS中的索引签名

一、概述

在大多数情况下,我们在使用对象前就可以确定对象的结构,并为对象中的属性添加准确的类型。

但是当我们无法确定对象中有哪些属性时,或者说对象中可以出现任意多个属性时,此时,我们就可以用索引签名类型了。

索引签名,主要是用来规定索引和属性值的类型,索引类型只能是 string 类型 或者 number 类型 或者 symbol 类型。

🔔 在 TypeScript 中,索引签名类型包括字符串索引签名类型数字索引签名类型

二、字符串索引签名类型

1. 语法格式

interface MyObj {
    [key: string]: number;
}

上面的这段代码中,使用 [key: string] 来约束该接口中允许出现的属性名称,其中 key 表示字符串索引的标识符,可以是任何有效的属性名,而 number 表示对应属性的值的类型,可以是任意类型。

2. 用法

interface MyObj {
    [key: string]: number;
}

let obj: MyObj = {
    a: 1,
    b: 2,
    c: 3,
    d: 4
}

如果对象中属性值的类型不是 number 类型,代码会报错。

interface MyObj {
    [key: string]: number;
}

let obj: MyObj = {
    a: 1,         // 🙆:正确
    b: 2,         // 🙆:正确
    name: "Echo", // 🙅报错:不能将类型“string”分配给类型“number”
    bool: true,   // 🙅报错:不能将类型“boolean”分配给类型“number”
};

3. 特点

  • 一旦定义了索引签名,那么必选属性和可选属性的类型都必须是它的类型的子集。
  • 返回的属性值的类型必须是索引签名指定的类型。
  • 可以与其他明确声明的属性共存。
  • 可以同时拥有多种类型的索引签名。
    // Ok
    interface Person {
        // 同时拥有 string 类型和 number 类型的索引签名
        [props: string]: string | number;
        // 可以定义其它属性,但是属性值的类型必须是索引签名类型的子集
        // 这里的属性值类型必须是 string 类型或者 number 类型
        name: string;
        age: number;
    }
    
    // Error
    interface User1 {
        [prop: string]: string | number;
        // 🙅报错:类型“string | undefined”的属性“name”不能赋给“string”索引类型“string | number”。
        // ‘name’的属性值类型虽然为’string‘是‘string | number’的子集,但是当其为可选时,值类型就变为‘string | undefined 类型。
        name?: string,
        age: number
    }
    
    // Error
    interface User {
        [props: string]: string | number;
        name: string;
        age: number;
        // 🙅报错:类型“true”的属性“bool”不能赋给“string”索引类型“string | number”。
        // 由于上面的索引签名规定了只能是 string 类型或者 number 类型,所以这里不能写其它类型
        bool: true;
    }
  • 可以访问对象中已存在的属性,如果访问对象中不存在的属性,会返回 undefined
    interface Person {
        [props: string]: string | number;
        name: string;
        age: number
    }
    
    const person: Person = {
        name: 'Echo',
        age: 26,
        city: 'GuangZhou',
        phone: 13000000000,
        gender: 'Male',
    }
    
    //访问存在的属性
    console.log(person.name);       // 输出:Echo
    console.log(person["age"]);     // 输出:26
    console.log(person.city);       // 输出:GuangZhou
    console.log(person["phone"]);   // 输出:13000000000
    console.log(person["gender"]);  // 输出:Male
    
    //访问不存在的属性
    console.log(person["address"]); // 输出:undefined

三、数字索引签名类型

当一个对象具有数字索引签名时,它意味着该对象可以使用数字来索引并获得相应的属性的值。

1. 语法

interface MyArr {
  [index: number]: string
}

上面的这段代码中,我们定义了 MyArr 接口,它具有索引签名。这个索引签名表示了当用 number 去索引 MyArr 时会得到 string 类型的返回值。

2. 用法格式

interface MyArr {
  [index: number]: string;
}

const arr: MyArr = ["Echo", "James", "John", "Steven"];
console.log(arr[0]); // Echo
console.log(arr[1]); // James
console.log(arr[2]); // John
console.log(arr[3]); // Steven

3. 特点

  • 可以同时拥有多种类型的索引签名。
  • 返回的属性值的类型必须是索引签名指定的类型。
    interface MyArr {
        [index: number]: string | number;
    }
    
    const arr: MyArr = [1, 'a', 2, 'b'];

四、注意事项

1、索引签名的键只能是 string、number、symbol 或者模板文本类型

interface MyObj {
    //🙅报错:索引签名参数类型必须是 “string”、“number”、“symbol”或模板文本类型。
    //这里参数类型为boolean类型了
    [key: boolean]: boolean;
}

2. 不能多次定义相同类型的索引签名

interface MyObj {
    //🙅报错: 类型“string”的索引签名重复。
    [key: string]: string;
    [key: string]: number;
}

五、索引签名类型 VS 泛型工具类型Record<K, T>

先看看下面的代码,我们发现索引签名类型和泛型工具类型 Record<K, T> 很相似:

const object1: Record<string, string> = { name: "Echo" };     // 正确
const object2: { [key: string]: string } = { name: "Echo" };  // 正确

但实际上它们两者之间还是有区别的:

1. 用途不同

  • 索引签名类型:主要用于定义具有动态属性的对象,允许通过字符串或数字索引来访问属性。

    interface MyObj {
        [key: string]: string | number
    }
    
    const obj: MyObj = {
        name: "Echo",
        age: 26
    }
    
    console.log(obj.name);   // Echo
    console.log(obj["age"]); // 26
  • Record<K, T>:主要用于生成一个新的类型,用于表示具有固定类型的属性集合,其中所有属性的值都具有相同的类型。

    interface Person {
        name: string;
        gender: string;
    }
    
    type NewPerson = Record<keyof Person, string>;
    const person: NewPerson = {
        name: "Echo",
        gender: "Male",
    }

2. 类型约束

  • 索引签名类型:对于索引签名,键的类型可以是 string、number、symbol 或者模板文本类型,值的类型可以是任意类型。

    interface MyObj {
        //🙅报错:索引签名参数类型必须是 “string”、“number”、“symbol”或模板文本类型。
        //这里参数类型为boolean类型了
        [key: boolean]: boolean;
    }

  • Record<K, T>:对于 Record<K, T>,类型参数 K 的类型可以是 string、number 或 symbol,可以接收字面量类型或者字面量类型组成的联合类型来作为参数,而类型参数 T 可以是任意类型。
    比如使用联合类型来作为 Key 值:

    type Person = Record<"name" | "age" | "gender" | "bool", string | number | boolean>;
    const person: Person = {
        name: "Echo",
        age: 26,
        gender: "Male",
        bool: true,
    }

posted on 2024-07-08 14:03  梁飞宇  阅读(39)  评论(0编辑  收藏  举报