【从零搭建税务自动化系统-前端篇】typescript keyof typeof 理解

今天在看别人写的代码的时候,有个keyof和typeof的代码看不懂,下面就通过查找原理之后做个记录。

export const themeAlgorithm = {
  light: defaultAlgorithm,
  dark: darkAlgorithm,
} as const
export type ThemeType = keyof typeof themeAlgorithm

其实最后一行可以换种写法:

export type AppThemeType = 'light' | 'dark'

想要理解 TypeScript 里 keyof typeof 是如何工作的,首先需要理解什么是 字面量类型(literal types) 和 联合类型(union types).

字面量类型(literal types)

Typescript 中的字面量类型是更具体的string, number或boolean类型。比如“Hello World”是一个string, 但是string类型不是“Hello World”, 所以“Hello World”是一个字面量类型。

一个字面量类型可以被这样定义:

type A = "hello"

这表示A类型的对象只能有一个字符串“hello“,并且没有其他string类型的值或其他任何类型的值,比如如下代码:

let greeting: A
greeting = "hello" // OK
greeting = "hi" // Error: Type "hi" is not assignable to type "hello"

字面量类型本身并不是很有用,但是当它和联合类型(union types),类型别名(type aliases), 类型保护(type guards)组合起来后,就很有用了。

联合类型

如果希望属性为多种类型之一,如字符串或数组,这时联合类型就派上用场了(它使用 |(竖线) 作为标记,如string|number)。联合类型可以理解为多个类型的并集。联合类型表示变量、参数的类型不是某个单一的类型,而可能是多种不同类型的组合。

下面是联合字面量类型的例子:

type A = "Hello" | "Hi" | "Welcome"

现在A类型对象的值可以是“Hello”,“Hi”或“Welcome”

let greeting: A
greeting = "Hello"   // OK
greeting = "Hi"      // OK
greeting = "Welcome" // OK
greeting = "Other"   // Error: Type '"Other"' is not assignable to type 'A'
类型缩减

如果定义的联合类型包含字符串字面量类型和string类型,会有什么效果呢?由于string类型是字符串字面量类型的父类型,所以最后会缩减为string类型。number和boolean在这种情况下也会发生类型缩减。

type UnionNum = 1 | number // 类型为number
type UnionStr = "str" | string // 类型为string
type UnionBoolean = false | boolean // 类型为boolean

在这种情况下,TypeScript会对类型进行缩减,将字面量类型去掉,保留原始类型。

但是这样也造成另一个问题:编译器只能提示我们定义的变量是那个原始的类型:

TypeScript提供了一种方式来控制类型缩减,只需给父类型添加”& {}" 即可:

此时,其他字面量类型就不会被缩减,在编辑器中字符串字面量str1,str等就可以自动提示出来了。

另外,当联合类型的成员是接口类型,并满足其中一个接口的属性是另一个接口属性的子集,这个属性也会进行类型缩减:

type UnionInterface = {age: "18"} | {age: "18" | "25", [key: string]: string}

由于“18”是“18”|“25”的子集,所以age属性的类型会变成“18”|“25” 

类型别名(Type Alias)

联合类型如果很多,每次都需要写很长的类型定义,如下面的例子:

function f1(command: string[] | string){
    let line = '';
    if(typeof command === 'string'){
        line = command.trim()
    } else {
        line = command.join(' ').trim()
    }
}

可以看到这个方法的参数类型是一个联合类型,如果下次我们有个方法f2的参数也是一样的联合类型,我们就可以把这个参数的类型抽离出来,这个就叫做类型别名:

type Command = string[] | string

// 再次定义方法是可以这样写了
function f1(command : Command) {
    // ....
}

function f2(param: Command) {
    // ....
}
keyof单独使用

假设现在有个类型T, keyof T 将会给你一个新类型,它是我们前面提到的联合字面量类型,并且组成它的字面量类型是T的属性名称。最后生成的类型是字符串的子类型。

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

在Person类型上使用keyof, 将会得到一个新类型,如下面代码所示:

type newType = keyof Person

newType 就是一个联合字面量类型(“name” | “age”| “location”),它是由Person的属性组成的类型。

现在就可以创建newType类型的对象了:

keyof typeof 同时使用

typeof运算符为你提供对象的类型,上面例子中的Person interface,我们已经知道它的类型,所以只需要在Person上使用keyof操作符。

但是,当我们不知道对象的类型,或者我们只有一个值,类似开头的场景:

const themeAlgorithm = {
  light: defaultAlgorithm,
  dark: darkAlgorithm
}

这就需要我们一起使用keyof typeof的时候了。

typeof  themeAlgorithm 给到你的类型是{light: string, dark: string}

接着 keyof   操作符给到你的是联合字面量类型 "light" | "dark",就像下面的代码描述的一样:

type ThemeType = keyof typeof themeAlgorithm

let applicationTheme: ThemeType
applicationTheme = "dark"           // OK
applicationTheme = "light"          // OK
applicationTheme = "otherTheme"     // Error Type '"otherTheme"' is not assignable to type '"light" | "dark"'

在enum上使用keyof typeof

在TypeScript中,enum在编译时被用作类型,用来实现常量的类型安全,但是他们在运行时被视为对象。这时因为当TypeScript代码被编译为Javascript时,他们会被转换为普通对象。比如:

enum ColorsEnum {
  white = '#ffffff',
  black = '#000000'
}

ColorsEnum在运行时作为一个对象存在,不是一个类型,所以我们需要一起使用keyof typeof这两个操作符,像下面代码展示的一样。

type Color = keyof typeof ColorsEnum
let color:Color
color = "white"      // OK
color = "black"      // OK
color = "red"        // ERROR Type '"red"' is not assignable to type '"white" | "black"'.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值