su37josephxia / frontend-interview

前端面试知识点
MIT License
159 stars 45 forks source link

TS中什么是索引类型 #152

Open su37josephxia opened 2 years ago

su37josephxia commented 2 years ago
interface Obj {
  [key:string] :any
}

function pick(o: Obj, names: string[]) {
  return names.map(n => o[n])
}
miracle-dx commented 2 years ago

使用索引类型,编译器就可以检查使用动态属性名的代码

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map((n) => o[n]);
}
let person = {
  name: 'tom',
  age: 11,
};
type Persion = typeof person;
let strings: (string | number)[] = pluck(person, ['name', 'age']);

上例中的keyof T为索引类型操作符,对于任何类型 T,keyof T的结果为T上已知的公共属性名的联合。 T[K]为索引访问操作符,在这里,类型语法反映了表达式语法,这意味着 person['name']具有类型 Person['name'] 编译器会检查 name,age是否真的是 Person的属性。

索引类型和字符串的索引签名

interface Example<T> {
  [x: string]: T;
}
let keys: keyof Example<number>;
let values: Example<number>[KeyType];
Tiffany-yuan commented 2 years ago

使用索引类型,编译器就能够检查使用了动态属性名的代码

let person = {
    name: 'musion',
    age: 35
}
function getValues(person: any, keys: string[]) {
    return keys.map(key => person[key])
}
console.log(getValues(person, ['name', age])) // ['musion', 35]
console.log(getValues(person, ['gender'])) // [undefined]

在上述例子中,可以看到getValues(persion, ['gender'])打印出来的是[undefined],但是ts编译器并没有给出报错信息,那么如何使用ts对这种模式进行类型约束呢?这里就要用到了索引类型,改造一下getValues函数,通过 索引类型查询索引访问 操作符:

function getValues<T, K extends keyof T>(person: T, keys: K[]): T[K][] {
  return keys.map(key => person[key]);
}
interface Person {
    name: string;
    age: number;
}
const person: Person = {
    name: 'musion',
    age: 35
}
getValues(person, ['name']) // ['musion']
getValues(person, ['gender']) // 报错:
// Argument of Type '"gender"[]' is not assignable to parameter of type '("name" | "age")[]'.
// Type "gender" is not assignable to type "name" | "age".

编译器会检查传入的值是否是Person的一部分。

几个类型操作符:

keyof T ---索引类型查询操作符。 表示类型T的所有公共属性的字面量的联合类型

T[K] --- 索引访问操作符。表示对象T的属性K所代表的类型

T extends U --- 表示泛型变量可以通过继承某个类型,获得某些属性

frllk commented 2 years ago

索引类型让静态检查能够覆盖到类型不确定(无法穷举)的”动态“场景

function pluck(o, names) {
  return names.map(n => o[n]);
}
7TingYu commented 2 years ago

索引类型

编译器能够通过索引类型检查使用了动态属性名的代码。

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[]

编译器会检查 name是否真的是 Person的一个属性。

类型操作符:

zcma11 commented 2 years ago

索引类型就是对象的 key,数组的 index 的类型。可以使用 keyof,typeof,in 关键字更灵活的定义类型。索引类型一般用于描述使用的对象和他的属性。

设置索引类型最简单的方法就是直接指定类型。

interface foo<T> {
  [key: string]: T
}

interface bar<T> {
  [index: number]: T
}

但是我们无法约束具体的 key 和 不同的值类型,所以我们可以借助 keyof 和 in

type bar = 'a' | 'b' | 'c'
type foo = {
  [K in bar]: number
}

let a: foo = {
  a: '1', // error
  b: 2,
  c: 4,
  d: 4 // error
}
type foo<T> = {
  [K in keyof T]: T[K] // 可以原封不动的表示 T 的结构
}

// 可以简单配合 typeof 使用
foo<typeof bar>
// 借助了泛型,通过 T 约束第二个参数的类型为其索引,
// 如果没有提供第一个参数的类型会根据第一个参数的 key 的联合类型。
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map((n) => o[n])
}
ruixue0702 commented 2 years ago

TS中什么是索引类型?

索引类型可以约束对象属性的查询和访问 配合泛型约束能够建立对象,对象属性,属性值之间的约束关系

function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
    return keys.map(key => obj[key])
}

目的:从obj对象中抽取的属性数组keys中的元素,一定得是obj对象中的属性

实现思路: