我试图以一种类型安全的方式从第一个错误中获得message分:

// In a real case this is returned from the backend
const mutationError: unknown = {
    sources: {
        errors: [
            {
                message: 'Hello from error message'
            }
        ]
    }
};

const sources = (
    typeof mutationError === 'object'
    && mutationError
    && 'sources' in mutationError
    && typeof mutationError.sources === 'object'
    && mutationError.sources
);

// "sources" is typed as "false | object | null"

const errors = (
    typeof sources === 'object'
    && sources
    && 'errors' in sources
    && Array.isArray(sources.errors)
    && sources.errors
);

// "errors" is typed as "false | any[] | null"

const firstError = errors && errors[0];

// "firstError" is typed as "any"

const message = (
    typeof firstError === 'object'
    && firstError
    && 'message' in firstError
    && typeof firstError.message === 'string'
    && firstError.message
);

但由于某些原因,message最终变成了any,并且找不到方法.

我做错了什么?

判断一下这个TS Playground.

推荐答案

你面临的问题是,Array.isArray()this call signature:

interface ArrayConstructor {
    isArray(arg: any): arg is any[];
}

这意味着它将参数缩小到any[],这是一个元素为the any type的array.但any是"传染性的",因为它吸收了所有其他类型(any & T只是any),所以它在这里帮不了你.此后,你会在任何地方得到any美元.

可以说,really应该使用the safe unknown type而不是any[]返回unknown[],但调用签名早于unknown的存在.虽然microsoft/TypeScript#26188会议上有人建议将打字标准库改为unknown(其鼓舞人心的例子恰好是Array.isArray()),但这样做将是一个重大的突破性变化.因此,不幸的是,这可能永远不会发生.

除非发生这种情况,否则你应该解决它.一种方法是提供你自己的返回unknown[]Array.isArray()版本.最简单的方法就是把函数包装起来:

function isArray(x: any): x is unknown[] {
    return Array.isArray(x)
}

现在当你使用它时,你会得到unknown分而不是any分:

const errors = (
    typeof sources === 'object'
    && sources
    && 'errors' in sources
    && isArray(sources.errors)
    && sources.errors
);
// const errors: false | unknown[] | null

const firstError = errors && errors[0];
// const firstError: unknown

这就表现得更好了,因为unknown & TT:

const message = (
    typeof firstError === 'object'
    && firstError
    && 'message' in firstError
    && typeof firstError.message === 'string'
    && firstError.message
);
// const message: string | false | null

Playground link to code

Typescript相关问答推荐

Angular -使用Phone Directive将值粘贴到控件以格式化值时验证器不工作

如何从具有给定键列表的对象类型的联合中构造类型

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

忽略和K的定义

具有动态键的泛型类型

有没有办法解决这个问题,类型和IntrinsicAttributes类型没有相同的属性?

更新为Angular 17后的可选链运算符&?&问题

如果字符串文字类型是泛型类型,则用反引号将该类型括起来会中断

是否可以从函数类型中创建简化的类型,go 掉参数中任何看起来像回调的内容,而保留其余的内容?

是否可以针对联合类型验证类型属性名称?

有没有可能为这样的函数写一个类型?

推断从其他类型派生的类型

RXJS将对键盘/鼠标点击组合做出react

缩小对象具有某一类型的任意字段的范围

创建一个函数,该函数返回一个行为方式与传入函数相同的函数

如何在Reaction Native中关注屏幕时正确更新状态变量?

如何使用IsEqual创建断言类型相等的实用程序

Typescript 和 Rust 中跨平台的位移数字不一致

有没有办法从不同长度的元组的联合中提取带有类型的最后一个元素?

在 TypeScript 中实现类型级别深度优先搜索