网站搜索

typescript - 类型 'string | number | null | undefined' 不能分配给类型 'null'


我正在尝试在 TypeScript 类中实现 C# 类对象初始化器。但是编译器显示以下错误。

错误:类型 'string | number | null | undefined' 不能分配给类型 'null'。错误:类型 'undefined' 不能分配给类型 'null'

以下是代码

type gender = 'Male' | 'female';

class Person {
    public name: string | null = null;
    public age: number | null = null;
    public gender: gender | null = null;

    constructor(
        param: Partial<Person>
    ) {
        if (param) {

            /**
             * Is key exist in Person
             * @param param key
             */
            const keyExists = (k: string): k is keyof Person => k in this;

            /**
             * Keys
             */
            const keys: string[] = Object.keys(param);

            keys.forEach((k: string) => {
                if (keyExists(k)) {
                    //k: "name" | "age" | "gender"

                    const value = param[k]; //value: string | number | null | undefined

                    //let a = this[k]; //a: string | number | null

                    this[k] = value; //error :  Type 'string | number | null | undefined' is not assignable to type 'null'.
                                    // Error :  Type 'undefined' is not assignable to type 'null'.
                }
            });

        }
    }

}

let a = new Person({
    age: 10
});

console.log(a);

下面是 tsconfig.json

{
    "compilerOptions": {
      "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
      "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
      "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
      "strict": true,                           /* Enable all strict type-checking options. */
      "strictNullChecks": true,              /* Enable strict null checks. */
      "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
      "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    }
  }

答案#1

你的问题实际上有两个方面:

  1. param 是 Partial<Person>,因此它可能不包含所有存在的键。然而,keyExists() 方法只会告诉编译器给定的键是否存在于 Person 中,所以最终 param[key] 会返回 undefined。
  2. 正如 @VLAZ 在评论中指出的,编译器无法智能地猜测,或将 value 的类型缩小到逐键的基础上。

这个解决方案可能不是最好的,但它是可行的:

  1. 你需要添加一个类型保护检查,如果 param[k] 是 undefined
  2. 你需要手动将 this[k] 的类型转换为 typeof value

尽管我通常避免使用手动类型转换,但在这种情况下这是相当合理且安全的,因为在转换时,你正在处理的 k 必须是 Person 类的成员,所以你只是向编译器提示“我知道我正在处理的具体类型,不用惊慌”。

这样,修改后的逻辑在 Array.prototype.forEach 回调中看起来是这样的:

keys.forEach((k: string) => {
  if (keyExists(k)) {
    const value = param[k];

    if (value === void 0)
      return;

    (this[k] as typeof value) = value;
  }
});

在 TypeScript Playground 上查看它。

个人而言,我不喜欢嵌套的if语句,因为它会让代码变得非常难以阅读,所以你也可以将上述代码重构为仅使用守卫子句:

keys.forEach((k: string) => {
  if (!keyExists(k))
    return;

  const value = param[k];

  if (value === void 0)
    return;

  (this[k] as typeof value) = value;
});

在 TypeScript Playground 查看。