Typescript 中有keyof那么如何实现 Valueof呢

1,305 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情 

本篇文章结合开发遇到问题来讲解Typescript中如何获取ValueOf

回顾:keyof是什么怎么用

字面量类型(Literal Type

要理解keyof首先搞搞明白字面量类型,字面量类型在ts中比较特殊,string,boolean, number 特殊主要体现在,

type a = 'hellow'

interface Config {
    a :'hello' | 'world' | 'lalala'; 
    isEnable:  true;
    margin: 0 | 2 | 4;
}

Config.a"hello" | "world" | "lalala"

hellostring

但是string不是hello

hello只是string里面的一个。

Config.a中的类型就是"hello" 或者"world" 或者 "lalala" 三个类型

Config.isEnable: true

类型就是true,只能为true类型

Config.margin: 0 | 2 | 4

类型就是0,2,4属于number但是只能是0或者2或者4

keyof

keyof T 将会得到一个T中的的属性名(key)组成的联合字面量类型,这些属性名都属于string类型

type Type1Props = {
    name: string
    age: number
    weight: number
    ...
}
type Type2Props = {
    align: string
    value: string
    ...
}
type Type3Props = {
    align: string
    value: string
    ...
}
interface ComTypes: {
   Type1 : Type1Props,
   Type2 : Type2Props,
   Type2 : Type3Props,
}

type KeyComType = keyof ComTypes //'Type1' | 'Type2' | 'Type3'

如上 KeyComType最终得到的是ComTypes的所有key组成的联合类型 'Type1' | 'Type2' | 'Type3'

let newTypeObject: KeyComType
newTypeObject = "Type1"           // OK
newTypeObject = "Type2"            // OK
newTypeObject = "Type3"       // OK
newTypeObject = "test"     // `Error...`

ValueOf如何实现。

猜想interface的定义是不是就是对象呢,我们能不能通过key获取到value呢 c试试

interface ComTypes: {
   Type1 : Type1Props,
   Type2 : Type2Props,
   Type3 : Type3Props,
}
type B = ComTypes['Type1'] 通过提示果然得到了我们想要的类型 Type1Props

那么valueof是不是可以通过

type ValueComTypes = ComTypes['Type1'] | ComTypes['Type2'] | ComTypes['Type3']
// Type1Props | Type2Props | Type3Props
 

image.png

我们惊奇的发现,keyof获取的是string类型的子类组成的联合类型。但是valueof获取的却是实实在在的类型不是string的子类 是不是很不错。

也可以简写为

type ValueComTypes = ComTypes['Type1' | 'Type2' | 'Type3']

继续简化

type ValueComTypes = ComTypes[keyof ComTypes]

于是得到了ValueOf的 **通用泛型表达式**

type ValueOf<T> = T[keyof T];

后面我们使用可以直接

type ValueComTypes = ValueOf<ComTypes>

至此我们的通用ValueOf方法完成

实践

上一篇文章中我们介绍了组件动态化,于是在动态化过程中对不同组件设置不同Props就成了一个问题

需求

根据type值限定componentProps的类型必须为type对应的类型 如type === Type1componentProps为Type1Props

具体实现

Page.tsx

const Page = () => {
    return (
        <AComponent itemsArray = {[
            {type: 'Type1', componentProps:{
                name: 'jack', 
                age: 20, 
                weight: 60} 
            },
            {type: 'Type2' componentProps: {
                align: 'left',
                value: '23'    
            }},
            {type: 'Type3' componentProps: {
                wight: 100,
                length: '200'
            }},
        ]}/>
    )
}
export default Page

AComponent.tsx

interface ComTypes: {
   Type1 : Type1Props,
   Type2 : Type2Props,
   Type3 : Type3Props,
}

type ItemType = keyof ComTypes

// T必须为Type1或者Type2或者Type3
type ItemProps<T extends ItemType> = {
    type: T
    componentProps?: ComTypes[T]// 根据T获取ComTypes中对应的TypeProps
    showItem?: boolean

}

// 遍历得到
//{'Type1':ItemProps<'Type1'>,  'Type2':ItemProps<'Type2'>, 'Type3':ItemProps<'Type3'> }
type AntPropsMap = {
    [u in ItemType]: ItemProps<u>
}

//调用前面定义的通用ValuesOf方法 获取所有values
type ItemArrayType = ValuesOf<AntPropsMap>

// 得到最终的组件props对应的类型
interface AComponentProps {
    itemsArray: ItemArrayType[]
}



const AComponent: FC<AComponentProps> = ({itemsArray}) => {

}
export default AComponent

总结

根据我们一步步推到得到最终通用ValueOf方法为

type ValueOf<T> = T[keyof T];

并且得到的最终value组合类型为实实在在的类型,并非keyof那样获取到的是string的子类型。