【问题标题】:Infering correct string literal in method在方法中推断正确的字符串文字
【发布时间】:2019-02-02 04:02:47
【问题描述】:

我需要帮助说服 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" 作为类型。

我怎样才能做到这一点?

【问题讨论】:

  • 我认为这是一个错误,你可以在 GitHub 上提交。
  • 谢谢,您的解决方案确实有效,我将在 Github 上打开一个问题。

标签: typescript


【解决方案1】:

我不能肯定地告诉你为什么推理没有像你期望的那样工作,我希望它会这样。 AllEvents&lt;O, M&gt; 应该符合"1"|"2"|"3" 的约束,这反过来应该让编译器推断E 的文字类型"1"。相反,它推断"1"|"2"|"3"

根据我的测试,问题在于在Extract&lt;keyof E, string&gt;; 中使用Extract 如果我们删除它,推理将按预期工作(尽管您的用例可能需要这样做)。

该错误的解决方法是重新排序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">;

Playground link

【讨论】:

    猜你喜欢
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    相关资源
    最近更新 更多