我遇到了以下问题:在我的应用程序中,我有一个Map,其中一组lambda函数与大约Role配对.每个lambda获取secondParameter并返回settings对象.

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;

I received this error while trying to calling the function returning from the map: TS2722: Cannot invoke an object which is possibly 'undefined'.
I tried to guard it as shown below, but it doesn't seem to work...

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

如答案here所示,似乎使用optional chaining operator就行了,所以下面的代码片段实现了一个工作警卫

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

为什么使用optional chaining operator构建的守卫可以工作,而使用if map.has()构建的守卫则不能?

推荐答案

has() method on MapMap实例中不起type guard method的作用.因此,调用map.has(x)map没有narrowing影响,因此不会影响对map.get(x)的后续调用.

microsoft/TypeScript#13086有一个开放的特性请求来支持这一点,但它还没有实现,因为,至少对于一个可变的、非ReadonlyMap的(参见Using ReadonlyMap<K, V> type),map.has(x)现在是true的事实并不意味着它将永远是true.AS mentioned by the TS team dev lead in this comment:"这比看起来要棘手much倍,因为你还必须定义has支票应该如何持续."

为适应这一点而进行的任何更改都需要在以下两行中正确执行:

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-THEN-VALID.所以你可以这样做:

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

或者你可以用optional chaining来让它更简洁:

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

Playground link to code

Typescript相关问答推荐

为什么缩小封闭内部不适用于属性对象,但适用于声明的变量?

打印脚本丢失函数的泛型上下文

如何使用一个字段输入对象,其中每个键只能是数组中对象的id属性,而数组是另一个字段?

是否存在类型联合的替代方案,其中包括仅在某些替代方案中存在的可选属性?

如何将别名添加到vitest配置文件?

如何在方法中正确地传递来自TypeScrip对象的字段子集?

如何在typescript中为this关键字设置上下文

Angular 自定义验证控件未获得表单值

如何正确调用创建的类型?

为什么Typescript只在调用方提供显式泛型类型参数时推断此函数的返回类型?

在正常函数内部调用异步函数

具有匹配功能的TS通用事件处理程序

无法缩小深度嵌套对象props 的范围

使用&reaction测试库&如何使用提供程序包装我的所有测试组件

Route.ts文件中未导出HTTP方法

如何正确键入泛型相对记录值类型?

Typescript循环函数引用

使用 fp-ts 时如何使用 TypeScript 序列化任务执行?

如何在Typescript 中定义具有相同类型的两个泛型类型的两个字段?

从函数参数推断函数返回类型