也许是通用TypeScript问题。 考虑这样一个简单的过滤代码:
interface User {
id: number;
name?: string;
}
const users: User[] = [
{ id: 1, name: 'aaa' },
{ id: 2 },
{ id: 3, name: 'bbb' },
];
users
.filter(user => Boolean(user.name))
// Object is possibly 'undefined'... why?
.map(user => console.log(user.name.length));
我在undefined
块中过滤了filter
,但是编译器坚持说Object is possibly 'undefined'
。
有人可以解释为什么TypeScript会表现出这种现象吗? 任何好的解决方法。
注意:我想使用
strictNullChecks
选项。
谢谢。
答案 0 :(得分:4)
TypeScript不够聪明,无法推断出您的过滤器暗示user.name
现在不能为假。
显然,Array#filter
有一个特定的重载,用于处理类型防护:
// From lib.es5.d.ts
interface Array<T> {
// ...
filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
// ...
}
这意味着,如果您传递类型保护函数进行过滤,则结果数组将是较窄的类型!
interface ConfirmedNameUser extends User {
name: string; // no ?
}
// snip
.filter((user): user is ConfirmedNameUser => Boolean(user.name)
// no longer errors, type of user is ConfirmedNameUser and not User
.map(user => console.log(user.name.length));
或者,使用较少的解决方案是使用undefined
非null断言运算符告诉它信任您不是!
:
.map(user => console.log(user.name!.length));
请注意,只有在100%确定TypeScript过于狂热并且“您知道得更好”时,才应谨慎使用!
操作。 !
仅使警告静音,它不执行任何类型的运行时检查(与建议的?.
和??
运算符相对)。
在不应该使用!
的地方可能会导致运行时TypeErrors。
答案 1 :(得分:2)
另一种解决方案是使用type guard作为过滤器回调(将arg is T
作为函数的返回类型),以便过滤器之后的调用具有正确的输入类型:
users
.filter((user): user is Required<User> => !!user.name)
.map(user => console.log(user.name.length));
在这种情况下,您可以使用内置的映射类型Required<T>
,因为只有名称是可选的。但是您也可以根据需要使用类似{ name: string }
的类型。
答案 2 :(得分:2)
这里的另一种方法是通知编译器您的过滤器充当type guard:
interface NamedUser extends User {
name: string;
}
users
.filter((user: User): user is NamedUser => Boolean(user.name)) //annotated callback
.map(user => console.log(user.name.length)); // okay now
我介绍了一种名为NamedUser
的类型,它与User
相同,但是其中肯定存在name
属性。然后,我将回调方法注释为users.filter()
作为类型保护。这将导致编译器选择以下filter()
defined in the standard library重载:
interface Array<T> {
filter<S extends T>(
callbackfn: (value: T, index: number, array: T[]) => value is S,
thisArg?: any
): S[];
}
因此,users.filter()
的返回值现在为NamedUser[]
,而不仅仅是User[]
。因此,随后的map()
调用会按预期工作。
有一个suggestion使编译器将布尔值回调(例如x => !!x
或x => Boolean(x)
)识别为类型保护。如果该建议或类似建议曾经得到实施,则您的原始代码可能会在没有任何警告的情况下进行编译。但是现在您必须手动对其进行注释。
希望有所帮助;祝你好运!
答案 3 :(得分:1)
如果您有一些使用命名用户的代码,建议您为他们创建一个类型:
type NamedUser = Required<User>
然后,您可以使用这种代码:
const namedUsers = users.filter(user => Boolean(user.name)) as NamedUser[]
namedUsers.forEach(user => console.log(user.name.length));
或者使用Aaron和jcalz建议的类型保护:
function isNamedUser(user: User): user is NamedUser {
return Boolean(user.name)
}
users
.filter(isNamedUser)
.forEach(user => console.log(user.name.length));