如何提示我无法控制的函数类型?

How to hint the type of a function I do not control?

解析 JSON 格式的字符串时出现 linter 错误:

let mqttMessage = JSON.parse(message.toString())

// ESLint: Unsafe assignment of an `any` value. (@typescript-eslint/no-unsafe-assignment)

我控制message的内容所以我想告诉TS,JSON.parse()出来的其实是一个Object。我该怎么做?

注意:我可以关闭警告,但我想了解是否有更好的方法来解决这个问题。

JSON.parse 不是通用的,因此我们无法提供通用参数来执行此操作。

你有几个选择。

简单的事情是,因为 JSON.parse returns any,你可以只定义你分配给它的类型:

let mqttMessage: MQTTMessage = JSON.parse(message.toString());

(我使用 MQTTMessage 作为适当类型的替代品。)

不过,这对每个人来说都不够安全,因为它假设字符串定义了您期望它定义的内容。而且它有一个问题,如果你在别处这样做,你会重复这个假设。

相反,您可以定义一个函数:

function parseMQTTMessageJSON(json: string): MQTTMessage {
    const x: object = JSON.parse(json);
    if (x && /*...appropriate checks for properties here...*/"someProp" in x) {
        return x as MQTTMessage;
    }
    throw new Error(`Incorrect JSON for 'MQTTMessage' type`);
}

那么你的代码是:

let mqttMessage = parseMQTTMessageJSON(message.toString());

问题是 JSON.parse returns 一个 any 类型。

这很公平 - TypeScript 不知道它是要解析为字符串、数字还是对象。

您有一个 linting 规则说 'Don't allow assigning variables as any'。

所以是的,您可以强制执行 JSON.parse

的结果
type SomeObjectIKnowAbout = {

}; 
const result = JSON.parse(message.toString()) as SomeObjectIKnowAbout; 

在这种情况下,我喜欢做的是创建一个特定的解析函数,它将在运行时断言 obj 确实是您所说的形状,并将进行类型转换,以便您可以同时处理它您正在将代码编写为该对象。

type SomeObjectIKnowAbout = {
    userId: string; 
}

type ToStringable = {
    toString: () => string; 
}

function parseMessage(message: ToStringable ) : SomeObjectIKnowAbout {
    const obj = JSON.parse(message.toString()); //I'm not sure why you are parsing after toStringing tbh. 

    if (typeof obj === 'object' && obj.userId && typeof obj.userId === 'string') {
        return obj as SomeObjectIKnowAbout; 
    }
    else {
        throw new Error ("message was not a valid SomeObjectIKnowAbout"); 
    }
}


作为类型断言和运行时包装函数的替代方法,您可以利用 declaration merging 为全局 JSON 对象增加 parse 方法的通用重载。这将允许您通过预期的类型并为您提供改进的 IntelliSense,以防您在解析时使用 reviver

interface JSON {
    parse<T = unknown>(text: string, reviver?: (this: any, key: keyof T & string, value: T[keyof T]) => unknown): T
}

type Test = { a: 1, b: "", c: false };

const { a, b, c } = JSON.parse<Test>(
    "{\"a\":1,\"b\":\"\",\"c\":false}",  
    //k is "a"|"b"|"c", v is false | "" | 1
    (k,v) => v
);

或者,如果您依赖 declaration files 来扩充全局接口:

declare global {
  interface JSON {
    parse<T = unknown>(text: string, reviver?: (this: any, key: keyof T & string, 
  value: T[keyof T]) => unknown): T
  }
}

Playground