我试图让Field
中的每个ObjectType
拥有自己的通用名称,因此每个Field的resolve
fn必须返回在type
属性中定义的类型
type Scalars = {
ID: string;
Int: number;
Float: number;
String: string;
Boolean: boolean;
};
type Field<T extends keyof Scalars> = {
type: T,
resolve: () => Scalars[T];
}
type ObjectType<T extends keyof Scalars> = {
[key: string]: Field<T>
}
const objectType = <T extends keyof Scalars>(config: ObjectType<T>): any => 'todo';
objectType({
id: {
type: 'Int',
// type of `resolve` is `() => (string | number)`, should be `() => number`
resolve: () => '123',
},
name: {
type: 'String',
// should be `() => string`
resolve: () => 123,
},
});
研究了其他一些SO问题之后,我在https://stackoverflow.com/a/51547767/11042710
中找到了几乎可以解决我的问题的解决方案这是我尝试上面建议的TS游乐场 TS playground。
但是当我尝试将infer P
类型传递给extends keyof Scalars
的泛型时失败了
答案 0 :(得分:2)
您的Field<..>
类型看起来可以像这样形成discriminated union:
type Fields = { [K in keyof Scalars]: Field<K> }[keyof Scalars];
这里Fields
的类型等同于
type Fields = Field<"ID"> | Field<"Int"> | Field<"Float"> |
Field<"String"> | Field<"Boolean">
其中,联合中的所有术语都有一个公用的type
字段,该字段的字符串文字类型可用于区分它们。很好,因为这意味着您的objectType()
方法可以减少很多通用的跳转,因为config
属性值可以限制为特定的类型Fields
:
const objectType = <T extends Record<keyof T, Fields>>(config: T): any => 'todo';
现在我们要做的就是将config
约束为具有任何键(Record<keyof T, ...>
)且其值均为Fields
的对象。让我们看看它是否有效。这是预期的用途:
objectType({
id: {
type: 'String',
resolve: () => "123",
},
ide: {
type: 'Int',
resolve: () => 123,
},
});
这是您做错了会发生的事情:
objectType({
id: { // error! number is not assignable to string
type: 'String',
resolve: () => 123,
},
ide: { // error! string is not assignable to boolean
type: 'Boolean',
resolve: () => "123",
},
});
那是您期望的错误,对吧?