typescript - Typescript中错误的自动返回类型推导
问题描述
在设置类中,我有一个获取值的方法,如果找不到给定的键,则返回一个可选的默认值:
/**
* 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 类型,而是 type 60
。我可以明确地将<number>
其用作模板/通用参数,get
然后结果就可以了,但是让 Typescript 推断出正确的类型会更酷。必须改变什么才能做到这一点?
更新
我刚刚在 Typescript 中找到了关于类型扩展的描述(在写这个问题时我不知道正确的术语)。事实证明,当将结果分配给get
可变变量时,类型被扩大了。否则,它保持为文字类型。
虽然这是有趣的信息,但它对这个问题没有帮助,因为如果在初始分配后没有更改,linter 通常会将任何转换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 中的 Literal Type Widening一文中了解更多相关信息。
没有办法(我知道)强制类型扩大,所以我在这里看到了 3 种可能的方法:
get
分配调用结果时使用可变目标。这可能是有问题的,因为如果没有其他赋值,linter 将尝试“优化”变量以使其变为不可变。向目标添加显式类型注释,例如:
const removeIdleTime: number = settings.get("workers.removeIdleTime", 60);
- 明确指定泛型参数:
const removeIdleTime = settings.get<number>("workers.removeIdleTime", 60);
或者
const removeIdleTime = settings.get("workers.removeIdleTime", 60 as number);
不过,所有这些方法都不能真正解决我的问题。
推荐阅读
- docker - 在具有 java 版本 1.8.0_282 的容器上设置 MaxRAMPercentage
- scala - 如何区分 Scala 中的脚本和普通类文件?
- sql-server - SQL Server:选择较新行的计数
- python - 如何禁用币安现货 API PRICE_FILTER?
- twitter-bootstrap - 如何在照片下方显示 2 个按钮?
- php - 是否可以检查 wp_enqueu_scripts 中呈现的页面内容?
- java - 与自定义 jTable 的单元格交互
- javascript - 使用 Javascript 中的函数从 API 调用返回 JSON 对象
- publish-subscribe - 建立数据订阅者。如何通过 IP 循环希望
- keycloak - 如何自定义 Keycloak 用户帐户?