在打字稿中使用类型对象的键来推断类型

时间:2020-03-19 23:46:55

标签: reactjs typescript

所以我有一个类型,我想根据传递给函数的键值动态更新其字段。但是,我要这样做的方式(brokenFunction)给我分配错误:类型'34'不能分配给类型'从不',类型'34'不能分配给类型'从不'。 workingFunction可以工作,但不那么方便。有谁有办法做到这一点?我猜它涉及泛型,但我仍然不明白为什么类型守卫arent工作.....

interface Todo {
  id: number;
  text: string;
}

const todo = {
  id: 1,
  text: "Buy milk",
};


function brokenFunction(x: Todo, field: keyof Todo): void {
  if (typeof field === 'number') {
    x[field] = 34
  } else if (typeof field === 'string') {
    x[field] = 'something else'
  }
}


function workingFunction(x: Todo, field: keyof Todo): void {
  if (field == 'id') {
    x[field] = 2345
  } else if (field == 'text') {
    x[field] = 'asdfsf'
  }
}

游乐场link

1 个答案:

答案 0 :(得分:1)

brokenFunction()中,field参数的类型为"id" | "text",表示它是字符串"id"或字符串"text"。在这两种情况下,在运行时,typeof field将是"string"。因此第一个if块将永远不会到达,而第二个总是会到达:

  if (typeof field === 'number') {
    x[field] = 34; // can never happen
  } else if (typeof field === 'string') {
    x[field] = 'something else'; // might be an error
  }

我想您可能是说类似的话,但这也不起作用:

function brokenFunction2(x: Todo, field: keyof Todo): void {
  if (typeof x[field] === 'number') {
    x[field] = 34; // error!
  } else if (typeof x[field] === 'string') {
    x[field] = 'something else'; // error!
  }
}

这是因为编译器不会执行您试图在此处进行的那种控制流分析;您希望编译器根据field来缩小typeof x[field]的类型,但并非如此。这与known issue, microsoft/TypeScript#10530有关,其中对属性的此类括号访问不会触发控制流分析变窄。哦,很好。

那么,我们该如何解决呢?一种可能的方法是制作一个user-defined type guard函数,该函数明确告诉编译器您要寻找的内容:

function fieldGuard<T, V>(
  x: T,
  k: keyof T,
  guard: (x: any) => x is V
): k is { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T] {
  return guard(x[k]);
}

函数fieldGuard()包含一个对象x,该对象k的键以及要对其属性x[k]进行检查的保护函数。然后根据保护的结果缩小键k的类型。然后,这个工作:

function okayFunction(x: Todo, field: keyof Todo): void {
  if (fieldGuard(x, field, (y): y is number => typeof y === 'number')) {
    x[field] = 34;
  } else if (fieldGuard(x, field, (y): y is string => typeof y === 'string')) {
    x[field] = "something else";
  }
}

这有点乏味,但是它非常接近您的原始逻辑,并且编译器愿意至少为您做一些检查。如果没有Microsoft / TypeScript#10530的修复程序,我将无法提供比上述更好的任何东西,或者像您的workingFunction()那样仅检查密钥本身。

好的,希望能有所帮助;祝你好运!

Playground link to code