繁体   English   中英

Typescript中错误的自动返回类型扣除

[英]Wrong automatic return type deduction in Typescript

在设置 class 中,我有一个获取值的方法,如果找不到给定的键,则返回一个可选的默认值:

    /**
     * Returns the value stored at the given key, which can have the form `qualifier.subKey`.
     *
     * @param key The key to look up.
     * @param defaultValue An optional value to be returned if the there's no value at the given key or the key doesn't
     *                     exist at all.
     *
     * @returns If a return value is given the return type is the same as that of the default value. Otherwise it's
     *          either undefined or the same as the type found at the given key.
     */
    public get<T>(key: string, defaultValue: T): T;
    public get(key: string): any;
    public get<T>(key: string, defaultValue?: T): T | undefined {
        const { target, subKey } = this.objectForKey(key, false);
        if (!target || !subKey) {
            return defaultValue;
        }

        return target[subKey] as T ?? defaultValue;
    }

这个实现给了我没想到的返回类型。 考虑这个电话:

const removeIdleTime = settings.get("workers.removeIdleTime", 60);

变量removeIdleTime不是我期望的类型 number ,而是类型60 我可以显式使用<number>作为模板/通用参数来get结果,然后结果就可以了,但是让 Typescript 推断出正确的类型会更酷。 必须改变什么才能做到这一点?

更新

我刚刚在 Typescript 中找到了关于类型扩展的描述(在写这个问题时我不知道正确的术语)。 事实证明,将get的结果分配给可变变量时,类型被扩大了。 否则它保持文字类型。

虽然这是一个有趣的信息,但它对这个问题没有帮助,因为如果在初始分配后它们没有更改,linters 通常会将任何let转换为const

解决方案

感谢@Etheryte,我找到了一个解决方案:有一种方法可以通过使用条件类型来强制扩展类型。

export type ValueType<T> = T extends string
    ? string
    : T extends number
        ? number
        : T extends boolean
            ? boolean
            : T extends undefined
                ? undefined
                : [T] extends [any]
                    ? T
                    : object;

可以这样使用(注意唯一的变化,返回类型):

    /**
     * Returns the value stored at the given key, which can have the form `qualifier.subKey`.
     *
     * @param key The key to look up.
     * @param defaultValue An optional value to be returned if the there's no value at the given key or the key doesn't
     *                     exist at all.
     *
     * @returns If a return value is given the return type is the same as that of the default value. Otherwise it's
     *          either undefined or the same as the type found at the given key.
     */
    public get<T>(key: string, defaultValue: T): ValueType<T>;
    public get(key: string): any;
    public get<T>(key: string, defaultValue?: T): T | undefined {
        const { target, subKey } = this.objectForKey(key, false);
        if (!target || !subKey) {
            return defaultValue;
        }

        return target[subKey] as T ?? defaultValue;
    }

这也适用于枚举,其中条件类型返回数字或字符串,具体取决于枚举的基本类型。


上一个答案

此行为是设计使然。 分配给常量目标的文字值保持其文字类型。 这遵循始终存储最窄类型的原则。 在值可以更改的情况下(例如,通过将文字分配给可变变量),Typescript 转换器会扩展类型以允许除初始文字之外的其他值。 您可以在 TypeScript 中的文字类型加宽一文中阅读更多相关信息。

没有办法(我知道)强制类型加宽,所以我在这里看到 3 种可能的方法:

  1. 分配get调用的结果时使用可变目标。 这可能会有问题,因为如果没有其他分配给它,linters 将尝试“优化”变量使其变得不可变。

  2. 向目标添加显式类型注释,例如:

const removeIdleTime: number = settings.get("workers.removeIdleTime", 60);

  1. 明确指定泛型参数:

const removeIdleTime = settings.get<number>("workers.removeIdleTime", 60);

要么

const removeIdleTime = settings.get("workers.removeIdleTime", 60 as number);

但是,所有这些方法并不能真正解决我的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM