在 TS 中如何安全的使用 JSON.parse()

我们来谈论下大家都比较熟悉的 JSON.parse(),大家都知道我们可以使用此方法来完成将一个 json 字符串解析为一个对象。比如

1
2
3
4
const jsonString = '{ "title": "Title" }'

const jsonValue = JSON.parse(jsonString)
// { title: 'Title' } ✅

问题

你是否想过咋样一个问题:当你搞乱 json 字符串的值时会发生什么?比如将上面代码示例中的 '{ "title": "Title" }'修改为 ''。那么 JSON.parse()会抛出一个错误:

1
2
3
4
const jsonString = ''

const jsonValue = JSON.parse(jsonString)
// Error: Unexpected end of JSON input ❌ 💣

这看起来是一个小问题,但是大多数的开发者可能忘记正确的处理这个问题。
在 TypeScript 中,JSON.parse() 没有任何特定类型参数, 它只返回 any类型。 这意味着,它是以失去静态类型和类型安全的优势为代价的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface JSON {
/**
* Converts a JavaScript Object Notation (JSON) string into an object.
* @param text A valid JSON string.
* @param reviver A function that transforms the results. This function is called for each member of the object.
* If a member contains nested objects, the nested objects are transformed before the parent object is.
*/
parse(text: string, reviver?: (this: any, key: string, value: any) => any): any;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
* @param value A JavaScript value, usually an object or array, to be converted.
* @param replacer A function that transforms the results.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
* @param value A JavaScript value, usually an object or array, to be converted.
* @param replacer An array of strings and numbers that acts as an approved list for selecting the object properties that will be stringified.
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
}

当开发人员将潜在不安全的数据视为完全安全时,可能会导致一些意想不到的问题。

解法

  1. 写一个 safeJsonParse函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const safeJsonParse = <T>(str: string) => {
    try {
    const jsonValue: T = JSON.parse(str);
    return jsonValue;
    } catch {
    return undefined;
    }
    };
    // const safeJsonParse: <T>(str: string) => T | undefined ✅
  2. 增加兜底

    1
    2
    3
    4
    5
    6
    7
    8
    interface JsonValue {
    title: string
    }

    const jsonString = '{ "title": "Title" }'

    const jsonValue = safeJsonParse<JsonValue>(jsonString) || { "title": "" }
    // const jsonValue: JsonValue ✅

    为了防止 jsonValue的值为 undefined,我们可以添加一个兜底机制。 这确保了无论发生什么情况,jsonValue始终是有值的。

    结论

    因此,如果我们正确的处理这些小问题,我们就能避免一些令人头疼的问题,并使我们的代码变得更加健壮。