这与我为@nexus/schema库(类型安全的 GraphQL)构建的插件有关,但它纯粹是 Typescript 输入问题。
我有一个规则系统,我的所有规则都是从这个接口派生的:
interface Rule<Type extends string, Field extends string> {
resolve(root: RootValue<Type>, args: ArgsValue<Type, Field>): boolean;
}
注意:RootValue
andArgsValue
是用于获取“真实”生成的类型或 return 的类型any
,这是 nexus 用于键入所有内容的技巧,而无需显式指定类型。
请参阅此链接以获取源代码。
最基本的两个是:
type Options = { cache?: boolean }
type RuleFunc<Type extends string, Field extends string> =
(root: RootValue<Type>, args: ArgsValue<Type, Field>) => boolean;
class BaseRule<Type extends string, Field extends string> implements Rule<Type, Field> {
constructor(private options: Options, private func: RuleFunc<Type, Field>) {}
resolve(root: RootValue<Type>, args: ArgsValue<Type, Field>) {
// Do stuff with the options
const result = this.func(root, args)
return result
}
}
class AndRule<Type extends string, Field extends string> implements Rule<Type, Field> {
constructor(private rules: Rule<Type, Field>[]) { }
resolve(root: RootValue<Type>, args: ArgsValue<Type, Field>) {
return this.rules
.map(r => r.resolve(root, args))
.reduce((acc, val) => acc && val)
}
}
然后我定义助手:
const rule = (options?: Options) =>
<Type extends string, Field extends string>(func: RuleFunc<Type, Field>): Rule<Type, Field> => {
options = options || {};
return new BaseRule<Type, Field>(options, func);
};
const and = <Type extends string, Field extends string>(...rules: Rule<Type, Field>[]): Rule<Type, Field> => {
return new AndRule(rules)
}
我的问题是我需要能够支持适用于所有类型/字段的通用规则和仅适用于一种类型/字段的特定规则。但是,如果我将通用规则与特定规则结合起来,则生成的规则就是Rule<any, any>
允许接受不良规则的规则。
const genericRule = rule()<any, any>((root, args) => { return true; })
const myBadRule = rule()<"OtherType", "OtherField">((root, args) => {
return true;
})
const myRule: Rule<"Test", "prop"> = and(
rule()((root, args) => {
return false
}),
genericRule,
myBadRule // THIS SHOULD BE AN ERROR
)
我猜这部分与 Typescript 中缺乏存在类型有关,这基本上迫使我首先使用 a any
,但是有没有一种解决方法可以用来防止类型 theany
覆盖我的类型。我发现的一种解决方法是显式键入and
,但从可用性的角度来看这并不好。
编辑 2:我创建了一个带有简化版本的游乐场,以便更容易查看问题。
编辑 3:正如评论中所指出的,never
适用于前面的示例。因此,我创建了这个never
不起作用的示例。我还修改了这个问题,以便所有信息都在问题中供后代使用。我还发现never
不能使用的原因是因为ArgsValue
类型。
非常感谢!
编辑1:
我找到了一种解决方法,尽管它需要更改界面:
export interface FullRule<
Type extends string,
Field extends string
> {
resolve(
root: RootValue<Type>,
args: ArgsValue<Type, Field>,
): boolean;
}
export interface PartialRule<Type extends string>
extends FullRule<Type, any> {}
export interface GenericRule extends FullRule<any, any> {}
export type Rule<Type extends string, Field extends string> =
| FullRule<TypeName, FieldName>
| PartialRule<TypeName>
| GenericRule;
随着and
成为:
export const and = <Type extends string, Field extends string>(
...rules: Rule<Type, Field>[]
): FullRule<Type, Field> => {
return new RuleAnd<Type, Field>(rules);
};
and
返回正确类型的,FullRule<'MyType','MyField'>
因此将拒绝badRule
. 但它确实需要我添加新方法来创建部分规则和通用规则。