为什么 `map.has()` 不充当类型保护

2023-12-28

我遇到了以下问题:在我的应用程序中,我有一个Map其中一组 lambda 函数与一些函数配对Roles。每个 lambda 需要一个secondParameter并返回一个settings object.

enum Role {
    ROLE_1 = "role"
}
const settingsRole1 = {}
const defaultSettings = {}
const secondParameter = ""

const map = new Map<Role, (secondParameter: string) => any>([
    [Role.ROLE_1, (secondParameter: string) => settingsRole1]
]);
const settings = map.get(Role.ROLE_1)(secondParameter) ?? defaultSettings;

我在尝试调用从地图返回的函数时收到此错误:TS2722: Cannot invoke an object which is possibly 'undefined'.
我尝试如下所示保护它,但似乎不起作用......

const settings = map.has(Role.ROLE_1) ? map.get(Role.ROLE_1)(secondParameter) : defaultSettings;

正如所回答的here https://stackoverflow.com/questions/76804217/cannot-invoke-value-of-map-because-possibly-undefined?noredirect=1#comment135401537_76804217,似乎使用optional chaining operator有效,所以下面的代码片段实现了一个工作守卫

const settings = map.get(Role.ROLE_1)?.(secondParameter) ?? defaultSettings;

为什么守卫要使用optional chaining operator工作,而构建的那个if map.has()才不是?


The has()方法上Map https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has不充当类型保护方法 https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates on the Map实例。所以打电话map.has(x) has no 缩小 https://www.typescriptlang.org/docs/handbook/2/narrowing.html作用于map,因此不会影响后续调用map.get(x).

有一个开放的功能请求:微软/TypeScript#13086 https://github.com/microsoft/TypeScript/issues/13086支持这一点,但它尚未实现,因为至少对于可变的、非ReadonlyMap (see 使用 ReadonlyMap 类型 https://stackoverflow.com/q/50046573/2887218),事实是map.has(x) is true现在并不意味着将来会是true永远。作为TS 团队开发负责人在此评论中提到 https://github.com/microsoft/TypeScript/issues/13086#issuecomment-524123373:“这么多,很多,很多,much比看起来更棘手,因为您还必须定义如何long the has检查应该持续。”

任何适应这一点的改变都需要在两个方面都正确行事map.get()下面几行:

const map = new Map([[1, { a: 1 }], [3, { a: 2 }]]);
if (map.has(2)) {
    map.get(2).a; // should be okay
    map.delete(2);
    map.get(2).a; // should not be okay
}

现在它们都出错了,这很烦人,但很安全。

你可以合并你自己的类型保护has()如果你愿意,也许像这样:

interface NarrowableMap<K, V> extends Map<K, V> {
    has<P extends K>(k: P): this is { get(p: P): V } & this;
}

但这样就不会在两者上出错,这很方便,但不安全,尤其是在第二行。

const map = new Map([[1, { a: 1 }], [3, { a: 2 }]]) as
    NarrowableMap<number, { a: number }>;

if (map.has(2)) {
    map.get(2).a; // no error, that's good
    map.delete(2);
    map.get(2).a; // no error, that's bad
}

该语言当前没有办法将方法标记为重置控制流缩小。这会在财产重新分配时自动发生,并且delete关键字,但映射操作不会触发此操作。

所以这就是为什么它不起作用。


最简单的重构就是放弃has-then-get并切换到get-然后-验证。所以你可以这样做:

const v = map.get(2);
if (v !== undefined) { v.a }

或者你可以使用可选链接 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining使其更简洁:

const w = v?.a
const x = map.get(2)?.a

Playground 代码链接 https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgQwA4wLwzAUwO4wLLIAUA2sQIwA0MA3jAgFwxkwC+AulcQMxW0MwAmVmzYBKANwBYAFABLAGYxCiJADoAFggiEBo0TRkwj8ZKoDmmKDtGqE4mAHoHMCOpABXADYATGACNMGBAAawQAT0NjFVVvTE9LTGspaWMTNQsrXVt7Jxc3L18wEFgAoNCI6RYZGVkwKEwAJ3kEYECAOQQGhpBsBD94giQAHgBpKgA1AD4YTAAPerBvCHxkUYnp6kijTQghgAUZ+cxF5ZHJwmDGPdFGKHVZZYeaGAzCJCubmHHWGAAyGDuD2SVWkMjACDgmAgSBa7U63WwmF8m1BKWMoEgsBUaAwOBWSBI5F4dEYzHYnB4z34QnYYjoEC2qRgHS6PT6A1WYHccACDWJ-C5PMarEmyVSCiU0R21n0KKZUVMryydkZqWisXi9SSqoV6Us1myjJBMmN0gx0BgADccdElRIaopCNaAISodDuRaYeS1JGyq22Vgyc2wXDoS0AflsQfAFtmNsV+t0kYQQA

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 `map.has()` 不充当类型保护 的相关文章

随机推荐