我需要说服Typescript来推断用户输入的字符串文字的帮助,而不是使用所有可能值的字符串文字。
看看这个例子(playground link):
// Supported methods.
type Methods = "GET" | "PUT" /* and etc. */;
// Event interface for each method.
interface Events {
[key: string]: [any, any];
}
// Event objects assigned to methods.
type MethodicalEvents = {
[key in Methods]: Events | undefined;
};
// Extract string keys only.
type EventKeys<E extends Events> = Extract<keyof E, string>;
// Extract all event keys from `MethodicalEvents -> Events`.
type AllEvents<O extends MethodicalEvents, M extends Methods> =
O[M] extends object ? EventKeys<O[M]> : never;
// Extract send value (first array value in `Events` interface).
type ExtractSendValue<O extends MethodicalEvents, M extends Methods, E extends AllEvents<O, M>> =
O[M] extends object ?
O[M][E] extends [any, any] ?
O[M][E][0] :
never :
never;
// Interface implementing stuff from above.
interface Foo extends MethodicalEvents {
PUT: {
"1": [123, void];
"2": [string, void];
"3": [boolean, void];
};
}
// Class for making requests via `Foo` interface.
class Bar<O extends MethodicalEvents> {
public request<M extends Methods, E extends AllEvents<O, M>>(
method: M,
event: E,
data: ExtractSendValue<O, M, E>,
) {
// Do stuff...
}
}
const bar = new Bar<Foo>();
// `true` should not be allowed.
bar.request("PUT", "1", true /*type: `string | boolean | 123`*/);
// type is `123`, as expected
type ExpectedType = ExtractSendValue<Foo, "PUT", "1">;
bar.request
的第二个参数的类型为"1" | "2" | "3"
,而我希望将"1"
作为类型。
我该如何实现?
答案 0 :(得分:1)
我不能肯定地告诉您为什么推论无法按您期望的那样进行,我希望如此。 AllEvents<O, M>
应该满足"1"|"2"|"3"
的约束条件,这反过来又应让编译器为E
推断文字类型"1"
。相反,它推断"1"|"2"|"3"
。
在我的测试中,问题出在Extract
中使用Extract<keyof E, string>;
,如果我们删除了该推断,则推断工作正常(尽管您的用例可能需要这样做)。
该错误的一种解决方法是重新排序AllEvent
中的条件。这似乎可行:
// Supported methods.
type Methods = "GET" | "PUT" /* and etc. */;
// Event interface for each method.
interface Events {
[key: string]: [any, any];
}
// Event objects assigned to methods.
type MethodicalEvents = {
[key in Methods]: Events | undefined;
};
// Extract all event keys from `MethodicalEvents -> Events`.
type AllEvents<O extends MethodicalEvents, M extends Methods> = Extract<O[M] extends object ? keyof O[M] : never, string>
// Extract send value (first array value in `Events` interface).
type ExtractSendValue<O extends MethodicalEvents, M extends Methods, E extends AllEvents<O, M>> =
O[M] extends object ?
O[M][E] extends [any, any] ?
O[M][E][0] :
never :
never;
// Interface implementing stuff from above.
interface Foo extends MethodicalEvents {
PUT: {
"1": [123, void];
"2": [string, void];
"3": [boolean, void];
};
}
// Class for making requests via `Foo` interface.
class Bar<O extends MethodicalEvents> {
public request<M extends Methods, E extends AllEvents<O, M>>(
method: M,
event: E,
data: ExtractSendValue<O, M, E >,
) {
// Do stuff...
}
}
const bar = new Bar<Foo>();
// `true` is not allowed
bar.request("PUT", "1", true /*type: `string | boolean | 123`*/);
// type is `123`, as expected
type ExpectedType = ExtractSendValue<Foo, "PUT", "1">;