我正在传递一个对象,该对象可能具有可选的 ket 值,例如:var1?: number
;我用过arrayEqual
在使用之前确保对象中存在所有键。
但是,我仍然得到 'input.var1' is possibly 'undefined'.
在const var1 = input.var1 + 3
.
如何解决?或者我应该在将其传递给 foo()
之前检查它(例如,在传递到 arrayEqual
之前使用 foo()
进行检查,这样我就可以删除 ?
中的所有 inputT
)?
interface inputT {
var1?: number;
var2?: number;
}
function arraysEqual(a1: string[], a2: string[]) {
return JSON.stringify(a1) == JSON.stringify(a2);
}
function foo(input: inputT): number {
if (
!arraysEqual(Object.keys(input), ["var1", "var2"])){
return 0
}
const var1 = input.var1 + 3
return var1
}
foo({"var1":2})
最佳答案
TypeScript 无法理解 arraysEqual(Object.keys(input), ["var1", "var2"])
对 input
的表观类型有任何影响。 narrowing TypeScript 的功能仅限于一小部分惯用的编码技术,这些技术需要在编译器中显式实现,而检查并不在其中。一旦您检查 Object.keys(input)
的结果编译器做任何事情都为时已晚,因为返回类型只是 string[]
与 input
无关。请参阅Why doesn't Object.keys return a keyof type in TypeScript?为什么。
在这种情况下,您可以采取的一种方法是编写自己的 custom type guard function 。您实现该函数,以便它执行您需要的检查,并为该函数提供一个调用签名,以表达该检查将如何影响该函数的参数之一。这基本上是您在编译器无法自行解决问题时告诉编译器如何缩小范围的一种方法。
由于您试图检查键数组中的所有值是否都存在于对象中,因此我们可以调用检查 containsKeys
,并给它一个调用签名,例如:
declare function containsKeys<T extends Partial<Record<K, any>>, K extends keyof T>(
obj: T, ...keys: K[]
): obj is T & Required<Pick<T, K>>;
这是一个 generic接受对象的函数 obj
通用类型 T
和一个(扩展)数组 keys
通用类型 K[]
,其中T
和K
是constrained这样K
是已知(可能)位于 T
中的所有键。返回值为obj is T & Required<Pick<T, K>>
(同时使用 Required
和 Pick
实用程序类型),这意味着 true
返回值意味着 obj
的类型可以从 T
缩小范围到 T
的版本其中键为 K
的所有属性已知存在。
它可以按照你喜欢的方式实现,尽管编译器只会相信你任何 boolean
-返回代码是可以接受的。因此我们可以将您的代码放入:
function containsKeys<T extends Partial<Record<K, any>>, K extends keyof T>(
obj: T, ...keys: K[]): obj is T & Required<Pick<T, K>> {
return JSON.stringify(Object.keys(obj)) == JSON.stringify(keys);
}
但这不是一个好的检查。它依赖于两种情况下键的顺序相同,而这并不是您真正可以保证的。相反,您应该考虑进行一些与顺序无关的检查;也许:
function containsKeys<T extends Partial<Record<K, any>>, K extends keyof T>(
obj: T, ...keys: K[]): obj is T & Required<Pick<T, K>> {
return keys.every(k => k in obj);
}
现在我们可以测试一下:
function foo(input: Input): number {
if (!containsKeys(input, "var1", "var2")) {
return 0
}
// input: Input & Required<Pick<Input, "var1" | "var2">>
// equivalent to { var1: number; var2: number; }
const var1 = input.var1 + 3
return var1
}
这有效。在初始 block 之后,input
已缩小范围为Input
至Input & Required<Pick<Input, "var1" | "var2">>
,一个看起来复杂的类型,相当于 {var1: number; var2: number}
...即与 Input
相同的类型使用必需的键而不是可选的键。所以input.var1
已知是 number
而不是number | undefined
.
关于typescript - 检查了可选的键值对象,但仍然得到 'xxx' 可能是 typescript 中的 'undefined',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76864696/