在typescript中,如何将一种类型和另外两种类型的键建立在一起

在typescript中,如何将一种类型和另外两种类型的键建立在一起,typescript,typescript-typings,typescript-generics,Typescript,Typescript Typings,Typescript Generics,设想一个函数getValidation接受某种状态和相应的模式来验证该状态。例如: type State = { selectedColor: string selectedSize: string selectedOptions?: Record<string, string> } type StateSchema = { selectedColor: { required: (val: any) => boolean } selectedS

设想一个函数getValidation接受某种状态和相应的模式来验证该状态。例如:

type State = {
  selectedColor: string
  selectedSize: string
  selectedOptions?: Record<string, string>
}

type StateSchema = {
  selectedColor: {
    required: (val: any) => boolean
  }
  selectedSize: {
    required: (val: any) => boolean
  }
  selectedOptions?: Record<string, { required: (val: any) => boolean }>
}

const state: State = {
  selectedColor: '',
  selectedSize: 'small',
}

const schema: StateSchema  = {
  selectedColor: {
    required: (val: any) => Boolean(val)
  },
  selectedSize: {
    required: (val: any) => Boolean(val)
  }
}

const validation = getValidation(
  schema,
  state
)

// validation
{
  $isValid: false,
  $value: {
    selectedColor: '',
    selectedSize: 'small',
  }
  selectedColor: {
    $isValid: false,
    $value: '',
    $validations: {
      required: false
    }
  },
  selectedSize: {
    $isValid: true,
    $value: 'small',
    $validations: {
      required: true
    }
  },
}

const state2 = {
  selectedColor: '',
  selectedSize: 'small',
  selectedOptions: {
    fit: 'tight',
    length: ''
  }
}

const schema2 = {
  selectedColor: {
    required: (val: any) => Boolean(val)
  },
  selectedSize: {
    required: (val: any) => Boolean(val)
  },
  selectedOptions: {
    fit: {
      required: (val: any) => Boolean(val)
    },
    length: {
      required: (val: any) => Boolean(val)
    }
  }
}

const validation2 = getValidation(
  schema2,
  state2
)

// validation2
{
  $isValid: false,
  $value: {
    selectedColor: '',
    selectedSize: 'small',
    selectedOptions: {
      fit: 'tight',
      length: ''
    }
  }
  selectedColor: {
    $isValid: false,
    $value: '',
    $validations: {
      required: false
    }
  },
  selectedSize: {
    $isValid: true,
    $value: 'small',
    $validations: {
      required: true
    }
  },
  selectedOptions: {
    $isValid: false,
    $value: {
      fit: 'tight',
      length: ''
    },
    fit: {
      $isValid: true,
      $value: 'tight',
      $validations: {
        required: true
      }
    },
    length: {
      $isValid: false,
      $value: '',
      $validations: {
        required: false
      }
    },
  },
}
类型状态={
selectedColor:字符串
selectedSize:字符串
所选选项?:记录
}
类型StateSchema={
所选颜色:{
必需:(val:any)=>布尔值
}
所选尺寸:{
必需:(val:any)=>布尔值
}
selectedOptions?:记录布尔值}>
}
常量状态:状态={
所选颜色:“”,
selectedSize:'小',
}
常量架构:StateSchema={
所选颜色:{
必需:(val:any)=>布尔值(val)
},
所选尺寸:{
必需:(val:any)=>布尔值(val)
}
}
const validation=getValidation(
模式,
状态
)
//验证
{
$isValid:false,
美元价值:{
所选颜色:“”,
selectedSize:'小',
}
所选颜色:{
$isValid:false,
$value:“”,
$validations:{
必填项:false
}
},
所选尺寸:{
$isValid:true,
$value:'小',
$validations:{
必填项:true
}
},
}
常量state2={
所选颜色:“”,
selectedSize:'小',
选择选项:{
适合:“紧”,
长度:“”
}
}
常量模式A2={
所选颜色:{
必需:(val:any)=>布尔值(val)
},
所选尺寸:{
必需:(val:any)=>布尔值(val)
},
选择选项:{
适合:{
必需:(val:any)=>布尔值(val)
},
长度:{
必需:(val:any)=>布尔值(val)
}
}
}
const validation2=getValidation(
方案2,
状态2
)
//验证2
{
$isValid:false,
美元价值:{
所选颜色:“”,
selectedSize:'小',
选择选项:{
适合:“紧”,
长度:“”
}
}
所选颜色:{
$isValid:false,
$value:“”,
$validations:{
必填项:false
}
},
所选尺寸:{
$isValid:true,
$value:'小',
$validations:{
必填项:true
}
},
选择选项:{
$isValid:false,
美元价值:{
适合:“紧”,
长度:“”
},
适合:{
$isValid:true,
$value:'紧',
$validations:{
必填项:true
}
},
长度:{
$isValid:false,
$value:“”,
$validations:{
必填项:false
}
},
},
}
以上示例需要注意的事项:

  • 状态可以是用户定义的任何对象
  • 模式结构必须与状态结构匹配,直到模式定义了一个对象,其中所有键都是验证状态中该点的函数
  • 结果验证结构应与状态结构匹配,并添加一些内容$isValid和$value将为状态对象的每个级别添加。如果模式定义了验证器对象,则应将相应的验证器密钥添加到$validations密钥中
如何为这样的模式编写泛型类型或接口,该模式依赖于另一种类型的结构(在本例中为state)


如何编写getValidation生成的验证的泛型类型或接口,这取决于状态和模式类型的结构

在你的问题中,我不确定你所说的“用户定义的任何对象”到底是什么意思,因为TypeScript类型只应用于编译时,而不是在运行时,因此如果用户只在运行时提供这些值,你就需要使用另一种方法

在这个回答中,我假设用户要么是使用您的框架的开发人员,要么是将用户所需的结构编码为TypeScript

您可以使用以下工具组合如下内容:

请注意,我在这里忽略了数组,并且在
状态下将您的选项从一个记录交换为一个普通对象
类型,但其工作原理应该是相同的

type SchemaEntry=ObjectSchemaEntry | PrimativeSchemaEntry;
类型PrimativeSchemaEntry={
[验证名称:字符串]:(val:T)=>布尔值;
}
类型ObjectSchemaEntry={
[P in keyof T]:SchemaEntry;
}
类型架构={
[P in keyof T]:SchemaEntry;
}
类型ValidationResultEntry=
S扩展了ObjectSchemaEntry?ObjectValidationResultEntry:
你的计划是什么?PrimativeValidationResultEntry:
从未;
类型PrimativeValidationResultEntry={
$isValid:boolean;
$value:T;
$validations:{
[P in keyof S]:布尔值;
};
};
类型ObjectValidationResultEntry={
[P in keyof T]:验证结果;
} & {
$isValid:boolean;
$value:T;
};
类型验证结果={
[P in keyof T]:验证结果;
} & {
$isValid:boolean;
$value:T;
};
函数inferStateTypeFrom(){
返回(状态:S):S=>state;
}
函数inferSchemaTypeFrom(){
return(schema:S):S=>schema;
}
然后你可以像这样使用它

类型状态={
selectedColor:字符串
selectedSize:字符串
选择选项?:{[键:字符串]:字符串}
}
const state=inferStateTypeFrom()({
所选颜色:“”,
selectedSize:'小',
选择选项:{
适合:“紧”,
长度:“”
}
});
const schema=inferSchemaTypeFrom()({
所选颜色:{
必需:(val)=>布尔值(val)
},
所选尺寸:{
必需:(val)=>布尔值(val)
},
选择选项:{
适合:{
foo:(val)=>布尔值(val)
},
长度:{
条形图:(val)=>布尔值(val)
}
}
});
常量结果:ValidationResult={
$isValid:false,
美元价值:{
所选颜色:“”,
selectedSize:'小',
选择选项:{
适合:'',
长度:“”
}
},
所选颜色:{
$isValid:false,
$value:“”,
$validations:{
必填项:false
}
},
所选尺寸:{
$isValid:true,
$value:'小',
$validations:{
必填项:true
}
},
选择选项:{
$isValid:false,
美元价值:{
适合:'',
长度:“”
},
适合:{
$isValid:true,
$value:“”,
$validations:{
傅:是的
}
},
长度:{
$isValid:false,
$v