所以我有一个类型,我想根据传递给函数的键值动态更新其字段。但是,我要这样做的方式(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
答案 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()
那样仅检查密钥本身。
好的,希望能有所帮助;祝你好运!