ts中的索引签名类型和映射类型

99 阅读3分钟

索引签名

1.什么是索引签名

索引可以理解为一个对象的属性。索引签名可以理解为一个定义一个对象属性的类型,可以是字符串和数字。

2.索引签名的作用是什么

当一个对象包含多个不确定的属性时,可以使用索引签名定义。

interface SquareConfig {
   color?: string;
   width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
   let newSquare = { color: "white", area: 100 };
   if (config.color) {
       newSquare.color = config.color;
   }
   if (config.width) {
       newSquare.area = config.width * config.width;
   }
   return newSquare;
}

image.png

思考:怎么解决上述报错? 方法一:使用类型断言绕开检查 let mySquare = createSquare({ colour: "red", width: 100 } as SquareConfig);

方法二:最佳的方式是能够添加一个字符串索引签名,前提是你能够确定这个对象可能具有某些做为特殊用途使用的额外属性。 如果 SquareConfig带有上面定义的类型的color和width属性,并且还会带有任意数量的其它属性,那么我们可以这样定义它:

interface SquareConfig {
    color?: string;
    width?: number;
    // 定义索引签名后SquareConfig可以有任意数量的属性,并且只要它们不是colorwidth,那么就无所谓它们的类型是什么
    [propName: string]: any;
}

3.索引签名类型

TypeScript支持两种索引签名:字符串和数字。

数字索引签名通常用于数组。

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

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。 也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。

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

// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
// Animal 是父类,Dog 是子类,所以[x: number]: Animal;不能定义父类
interface NotOkay {
   [x: number]: Animal;
   [x: string]: Dog;
}

image.png

如果改成下面这样则是可行的,将数字索引类型定义在Dog这个子类上

image.png

4.操作符

keyof 索引类型查询操作符

官网示例:

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] { 
    return names.map(n => o[n]); 
} 
interface Person { 
    name: string; 
    age: number; 
} 
let person: Person = { name: 'Jarid', age: 35 }; 
let strings: string[] = pluck(person, ['name']); // ok, string[]

keyof T的结果为 T上已知的公共属性名的联合.

T[K], 索引访问操作符

映射类型

Readonly<T> 将对象的所有属性都设置成只读的

type Readonly<T> = { 
    readonly [P in keyof T]: T[P]; 
}

Partial<T> 将对象的所有属性都设置为可选的

type Partial<T> = { 
    [P in keyof T]?: T[P]; 
}

Pick<Person,'name' | 'age'> 返回对象中的部分属性

type Pick<T, K extends keyof T> = { 
    [P in K]: T[P];
} 

eg:

interface Person {
    name: string;
    age: number;
    sex:'女'
}

type Obj = Pick<Person,'name' | 'age'>
const obj:Obj = {
    name:'bwf',
    age:19,
}

Record 将 K 中的所有属性值都转换为 T 类型,并返回新的对象类型

type Record<K extends string, T> = { 
    [P in K]: T; 
}

eg:

type TKeys = 'A' | 'B' | 'C'

interface People {
  name:string,
  age?: number,
  sex: string
}

type TRecord = Record<TKeys, People>

// 等同于
type TRecord = {
  B: People;
  C: People;
  A: People;
}