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