Ts keyof

索引查询 keyof

keyof可以用于获取某种类型的所有键,其返回类型是联合类型

接口

interface Person {
  name: string;
  age: number;
  location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

class Person {
    name: string = 'Mike';
}

let sname = keyof Person
sname = 'name'

如果把sname = 'name' 改为sname = 'yui'的话,TypeScript 编译器会提示以下错误信息:

Type '"yui"' is not assignable to type '"name"'.

解释:

keyof Person 获取到的类型是'name'

举个简单的例子:

let a : 1
a = 1
a = 2

以上的例子,将a的类型为1,那么这个变量的值只能是1,不能为其他的,当a = 2执行的时候,就会报错

基本数据类型

let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"

此外 keyof 也称为输入索引类型查询,与之相对应的是索引访问类型,也称为查找类型。在语法上,它们看起来像属性或元素访问,但最终会被转换为类型:

type P1 = Person["name"];  // string
type P2 = Person["name" | "age"];  // string | number
type P3 = string["charAt"];  // (pos: number) => string
type P4 = string[]["push"];  // (...items: string[]) => number
type P5 = string[][0];  // string

作用

function prop(obj: object, key: string) {
  return obj[key];
}

在上面代码中,为了避免调用 prop 函数时传入错误的参数类型,我们为 obj 和 key 参数设置了类型,分别为 {}string 类型。然而,事情并没有那么简单。针对上述的代码,TypeScript 编译器会输出以下错误信息:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.

原因:元素隐式地拥有 any 类型,因为 string 类型不能被用于索引 {} 类型。

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

在以上代码中,我们使用了 TypeScript 的泛型和泛型约束。首先定义了 T 类型并使用 extends 关键字约束该类型必须是 object 类型的子类型,然后使用 keyof 操作符获取 T 类型的所有键,其返回类型是联合类型,最后利用 extends 关键字约束 K 类型必须为 keyof T 联合类型的子类型。

type Todo = {
  id: number;
  text: string;
  done: boolean;
}

const todo: Todo = {
  id: 1,
  text: "Learn TypeScript keyof",
  done: false
}

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const id = prop(todo, "id"); // const id: number
const text = prop(todo, "text"); // const text: string
const done = prop(todo, "done"); // const done: boolean

如果访问todo对象中不存在的属性,那么就会报错

const date = prop(todo, "date");

//Argument of type '"date"' is not assignable to parameter of type '"id" | "text" | "done"'.

typeof与keyof的结合

const COLORS = {
  red: 'red',
  blue: 'blue'
}

// 首先通过typeof操作符获取color变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue',取自于k
type Colors = keyof typeof COLORS 
let color: Colors;
color = 'red' // Ok
color = 'blue' // Ok

// Type '"yellow"' is not assignable to type '"red" | "blue"'.
color = 'yellow' // Error

T[K] 索引访问

interface Eg1 {
  name: string,
  readonly age: number,
}
// string
type V1 = Eg1['name']
// string | number
type V2 = Eg1['name' | 'age']
// any
type V2 = Eg1['name' | 'age2222']
// string | number
type V3 = Eg1[keyof Eg1]

T[keyof T]的方式,可以获取到T所有key的类型组成的联合类型;

T[keyof K]的方式,获取到的是T中的key且同时存在于K时的类型组成的联合类型;

注意:如果[]中的key有不存在T中的,则是any;因为ts也不知道该key最终是什么类型,所以是any;且也会报错

& 交叉类型注意点

交叉类型取的多个类型的并集,但是如果相同key但是类型不同,则该keynever

interface Eg1 {
    name:string,
    age:number
}

interface Eg2 {
    name:string,
    age:string,
    color: string,
}

以上两个接口定义的name,age属性名是相同的,但是属性的类型并不相同

type T = Eg1 & Eg2

T的类型为 {name: string; age: never; color: string}

注意,age因为Eg1和Eg2中的类型不一致,所以交叉后age的类型是never

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值