Typescript中的类型联动
问题说明
类型联动,指的是一个对象中,A属性的值变化,会引起B属性的类型变化。
假设有这样一个类型:
1type Component = { 2 type: 'button' | 'input' | 'select' | 'textarea'; 3 payload: { 4 onChange: Function; 5 onClick: Function; 6 type: string; 7 placeholder: string; 8 }; 9}
类型联动,指的是当 type 的值发生变化时,payload的类型也跟着变化,比如当type为'input'时,可能希望payload中包含onChange,而当type为'button'时,则不希望payload中包含onChange。
粗糙的解决方案
最粗糙的解决方案,是用 Union 的形式,将多种类型组合起来。
类似下面的形式:
1type ButtonComponent = { 2 type: 'button'; 3 payload: { 4 onClick: Function; 5 type: string; 6 } 7} 8 9type InputComponent = { 10 type: 'input'; 11 payload: { 12 onChange: Function; 13 placeholder: string; 14 } 15} 16 17// ... 将所有可能的类型写一遍 18 19// 最后,给一个union类型 20type Component = ButtonComponent | InputComponent | ... | OtherComponents 21// => {type: 'button', payload: {onClick: Function, type: string}} | {type: 'input', payload: {onChange: Function, placeholder: string}}
这种方法比较笨拙,因为它需要把每种可能的组合都写一遍。
手写类型的时候,还可以勉强用一下,当遇到需要类型推导的场景时,就不好使了。
类型推导
使用如下的代码,可以推导出我们想要的类型
1// 先定义一个类型映射关系 2type ComponentPayload = { 3 button: { 4 onClick: Function; 5 type: string; 6 } 7 8 input: { 9 onChange: Function; 10 placeholder: string; 11 } 12 13 // 其它略过不写 14} 15 16// 使用 keyof 获取到所有的 key 17type ComponentPayloadKeys = keyof ComponentPayload; 18// => 'button' | 'input' 19 20// 再结合类型推导,构造出valueof 21type ComponentPayloadValues = ComponentPayload[keyof ComponentPayload] 22// => { onClick: Function; type: string; } | { onChange: Function; placeholder: string; } 23 24// 如果先构造出 {type => {type, payload}} 的格式,就可以用上面的valueof的方式,获取到目标类型 25type Component = { 26 [T in keyof ComponentPayload]: { 27 type: T; 28 payload: ComponentPayload[T] 29 } 30}[keyof ComponentPayload] 31// => {type: 'button', payload: {onClick: Function, type: string}} | {type: 'input', payload: {onChange: Function, placeholder: string}}
至此,就大功告成了。
总结
TS中的类型跟随值变化,本质是还是类型跟随类型变化,只是TS中允许把值作为类型的一种。
通过keyof、Generic
等操作生成Union类型,可以实现类型之间的联动。