【问题标题】:How to use typeof for conditional assigment to object value in typescript?如何使用 typeof 对打字稿中的对象值进行条件赋值?
【发布时间】:2019-12-01 11:16:53
【问题描述】:

我有一个对象,其中一些值是字符串和一些数字。

我想修剪所有字符串值。

type OptionalParams = {
  a?: string;
  b?: string;
  c?: number;
};
const optionalParams: OptionalParams = {};

const keys = Object.keys(optionalParams) as Array<keyof OptionalParams>;

keys.forEach((key: keyof OptionalParams) => {
  const paramValue = optionalParams[key];
  if (typeof paramValue === 'string') {
    const newValue = (paramValue as string).trim();
    optionalParams[key] = newValue as keyof OptionalParams;
  }
});

但我得到 ts-error:

键入'"a" | "b" | "c"' 不能分配给类型 'undefined'。

类型“a”不能分配给类型“未定义”。 ts(2322)

如果我删除“c”属性,我不会得到它,但我需要它。我用typeof加了if,但是typescript看不懂。

我的 tsconfig:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "declaration": true,
    "outDir": "./lib",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "**/__tests__/*"]
}

【问题讨论】:

    标签: javascript typescript types


    【解决方案1】:

    我不知道为什么,但是如果你把键转换成字符串就可以了

    type OptionalParams = {
      a?: string;
      b?: string;
      c?: number;
    }
    const optionalParams: OptionalParams = {
      a: 'some ',
      c: 1
    }
    
    const keys = Object.keys(optionalParams) as Array<keyof OptionalParams>
    
    keys.forEach((key) => {
      const paramValue = optionalParams[key]
      if (typeof paramValue === 'string') {
        const newValue = paramValue.trim()
        optionalParams[key as string] = newValue
        optionalParams[key as 'a' | 'b'] = newValue
      }
    })
    
    console.log('optionalParams', optionalParams)
    

    结果

    ❯ ts-node demo.ts
    optionalParams { a: 'some', c: 1 }
    

    Tsconfig

    {
      "compilerOptions": {
        "noEmit": true,
        "lib": ["dom", "esnext"],
        "jsx": "react-native",
        "moduleResolution": "node",
        "allowSyntheticDefaultImports": true,
        "skipLibCheck": true
      }
    }
    

    【讨论】:

    • 我认为你有一个不同的配置文件 tsconfig.json,因为我的解决方案有错误:元素隐式具有“任何”类型,因为“字符串”类型的表达式不能用于索引键入“可选参数”。在类型“OptionalParams”.ts(7053) 上找不到带有“字符串”类型参数的索引签名
    • 我建议尝试将key缩小"a" | "b",而不是扩大string。可能有一些方法可以在没有类型断言的情况下执行此操作,但如果您使用的是断言,我会这样做。
    【解决方案2】:

    问题是key 的类型是keyof OptionalParams,而您可以安全地写入OptionalParams[keyof OptionalParams] 的唯一值是undefined。 (您可以安全地阅读string | number | undefined)。编译器不明白您的typeof 检查对key 的类型有任何影响,因此它所看到的只是您正在将string 写入一个只能安全地采用undefined 的变量,并且它抱怨。

    在 TS3.4 及更低版本中未执行对索引访问属性的安全写入。这种行为是由于 changereleased with TypeScript 3.5

    解决这个问题的方法是让编译器相信keykeyof OptionalParams 的子集,这样您就可以向它写入string 值。这是让编译器确定哪些键适合写入特定类型的值的一种方法:

    type KeysForWriting<T extends object, V> = {
      [K in keyof T]-?: [V] extends [T[K]] ? K : never
    }[keyof T];
    
    type KW = KeysForWriting<OptionalParams, string>; // "a" | "b"
    

    在这里进行的一种方法是使用type assertion 通知编译器,在检查typeof 之后,您确定key 的类型为KeysForWriting&lt;OptionalParams, string&gt;

    keys.forEach(key => {
      const paramValue = optionalParams[key];
      if (typeof paramValue === "string") {
        const newValue = paramValue.trim();
        optionalParams[key as KeysForWriting<OptionalParams, string>] = newValue;
      }
    });
    

    没有错误。


    你可以做的另一件事是将你的代码抽象成一个更generic的属性修改函数,它利用unsafe but still-intentional behavior,从而允许你将T[K]类型的值写入@类型的变量987654346@,即使K 可能是键的联合(这是它可能不安全的方式之一)。像这样:

    function modifyProp<T, K extends keyof T, V extends T[K]>(
      t: T,
      k: K,
      check: (x: T[K]) => x is V,
      cb: (x: V) => T[K]
    ) {
      const orig = t[k];
      if (check(orig)) {
        t[k] = cb(orig);
      }
    }
    

    这里modifyProp(t, k, check, cb) 接受一个对象t、一个键k、一个type guard functioncheck 和一个回调cb。该实现检查t[k] 是否通过类型保护check,如果是,则通过回调修改其值。在您的情况下,您要做的检查是“这是string”:

    function isString(x: any): x is string {
      return typeof x === "string";
    }
    

    然后你的电话就变成了单线:

    keys.forEach(key => {
      modifyProp(optionalParams, key, isString, x => x.trim());
    });
    

    好的,希望其中一个对您有所帮助。祝你好运!

    Link to code

    【讨论】:

      猜你喜欢
      • 2019-09-18
      • 2019-06-03
      • 2021-04-25
      • 2019-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-06
      相关资源
      最近更新 更多