Typescript 类型脚本循环类型化对象
我有这个验证文件Typescript 类型脚本循环类型化对象,typescript,loops,types,typescript-typings,Typescript,Loops,Types,Typescript Typings,我有这个验证文件 interface Rule { required?: true; isNumber?: true; isDate?: { format: string; } // ... some other rules // ruleNames: RuleOptions } const RuleMap: { [key in keyof Required<Rule>]: ( value: any, options: Required&
interface Rule {
required?: true;
isNumber?: true;
isDate?: {
format: string;
}
// ... some other rules
// ruleNames: RuleOptions
}
const RuleMap: {
[key in keyof Required<Rule>]: (
value: any,
options: Required<Rule>[key]
) => boolean;
} = {
required: (value) => value != null,
isNumber: (value) => isNumber(value),
isDate: (value, options) => isDate(value, options),
//...otherRules
}
// now I have a fairly strong typed object RuleMap
RuleMap.isDate('foo', { format: "DD/MM/YYYY" }); // no problem
RuleMap.required('foo', { format: "DD/MM/YYYY" }); // throws error
e、 g:
指定isDate
时,RuleMap
强制我为函数输入确切的isDate
选项
当循环执行规则
时,我希望typescript知道我正在将确切的规则名
的规则选项传递给函数
可能吗?如果是这样的话,我应该怎么做?问题恰恰在于说明
myRules
是Rule
,这意味着TS在编译时不知道该对象中有哪些字段,它将其视为Rule
,因此字段可以存在,也可以不存在
您的循环是通过Rule
而不是typeofmyrules
中的所有键来输入的,实际上它们的一部分不在那里,但是type system没有看到这一点。在值级别,很明显,您要通过myRules
,如果您这样做,字段就在那里,但是对于类型级别myRules
,它只是Rule
,并且具有可以不定义值的键。这与以下简化示例中发生的情况完全相同:
const ruleName: "required" | "isNumber" | "isDate" = 'isDate';
const value = myRules[ruleName]; // value can be undefined!
以上就是在forEach
中发生的事情,键被键入为规则的键,值可以是未定义的
与被调用函数的要求冲突的,要求被声明为-选项:必需的[key]
,因此它不允许未定义的
。这是一个错误
好的,那么我们能做些什么呢?简单的解决方案是只使用const
直接类型的myRules
:
const myRules = {
isNumber: true
} as const; // pay attention here
(Object.keys(myRules) as (keyof typeof myRules)[]).forEach((ruleName) => {
const value = 1; // example
RuleMap[ruleName](value, myRules[ruleName]);
});
由于ruleName
始终在类型中,因此myRules[ruleName]
永远不会未定义,因此可以正常工作const
允许type system使用与运行时相同形状的对象
我想你需要在更真实的环境中使用它。在这种情况下,我们需要有通用的,这将允许as使用精确的规则
接口的狭窄类型。考虑下面的例子:
type OnlyKeysInRule<T extends Rule> = {
[K in keyof Rule]: T[K]
} & {
[K in Exclude<keyof T, keyof Rule>]: never
}
function doStaff<R extends Rule>(rules: OnlyKeysInRule<R>) {
(Object.keys(rules) as (keyof OnlyKeysInRule<R> & keyof typeof RuleMap)[]).forEach((ruleName) => {
const value = 1; // example
RuleMap[ruleName](value, rules[ruleName]);
});
}
doStaff(myRules) // is ok
doStaff({isNumber: true, isHelicopter: false}) // error as expected - `isHelicopter` is not define in RuleMap
type OnlyKeysInRule={
[K in keyof规则]:T[K]
} & {
[K in Exclude]:从不
}
函数doStaff(规则:OnlyKeysInRule){
(Object.keys(rules)as(keyof OnlyKeysInRule和keyof typeof RuleMap)[])。forEach((ruleName)=>{
const value=1;//示例
RuleMap[ruleName](值,规则[ruleName]);
});
}
doStaff(myRules)//可以吗
doStaff({isNumber:true,ishlicopter:false})//预期错误-`ishlicopter`未在规则映射中定义
说明:
OnlyKeysInRule
是唯一的映射,表示-您可以传递在规则中只有字段的对象。为什么它很重要-因为R扩展规则
意味着类型R可以有附加字段,这意味着RuleMap[ruleName]
操作不安全,因为ruleName
可以是RuleMap
中不存在的附加字段,所以此类型可以防止此类操作
as(keyof OnlyKeysInRule&keyof typeof RuleMap)
我们说我们只通过RuleMap
和R
中的键进行循环。在这里,TS无法推断OnlyKeysInRule
已经足以防止这种情况,这就是为什么需要使用union
这里的问题是typescript正在工作,它抛出了一个错误编译,因为如果不检查类型,就不能在同一个循环中为不同的规则应用相同的参数。如何让typescript知道我正在传递正确的规则?事实上,我通过了你想要的正确规则。谢谢!我真的需要那个真实环境的例子!
type OnlyKeysInRule<T extends Rule> = {
[K in keyof Rule]: T[K]
} & {
[K in Exclude<keyof T, keyof Rule>]: never
}
function doStaff<R extends Rule>(rules: OnlyKeysInRule<R>) {
(Object.keys(rules) as (keyof OnlyKeysInRule<R> & keyof typeof RuleMap)[]).forEach((ruleName) => {
const value = 1; // example
RuleMap[ruleName](value, rules[ruleName]);
});
}
doStaff(myRules) // is ok
doStaff({isNumber: true, isHelicopter: false}) // error as expected - `isHelicopter` is not define in RuleMap