我有一个接口,该接口的属性可以为null。 我要检查它是否不为null,然后将该对象传递给类型安全对象
在将属性分配给变量时,似乎可以缩小范围 但是当尝试整体使用该对象时,无法确定它不能为null。
我不想使用强制转换,因为这样会破坏设计时间类型检查的目的。
interface Person
{
midddle:string | null
}
interface MiddleNamePerson
{
midddle:string
}
function DoWork(person:Person) {
if(person.midddle)
{
const middleName:string = person.midddle; // works
const middle : MiddleNamePerson = person // Error: Type of 'Person' not Assignable to 'MiddleNamePerson'
DoStuff(person) // Error: the argument of 'Person' is not Assignable to parameter
}
}
function DoStuff(value:{midddle:string}) {}
答案 0 :(得分:2)
解决方案
替换此简单检查:
if(person.midddle)
具有更好的类型防护:
if(hasDefined(person, ['midddle']) {
这种类型保护可以定义为:
const hasDefined = <T, K extends keyof T>(argument: T | Defined<T, K>, keys: K[]): argument is Defined<T, K> =>
keys.every(key => argument[key] != null)
type Defined<T, K extends keyof T = keyof T> = {
[P in K]-?: Exclude<T[P], undefined | null>
}
说明
控制流在TypeScript中不起作用。通过检查if(person.midddle)
,我们知道中间名称是真实的,但是Person
的定义不受影响。仍然是一个对象,其中名为middle
的属性可以为null
。
通过更改类型保护程序,使其不验证单个字段,而是验证整个对象,我们可以确保person
是整个代码块中定义明确的Person
。 / p>
答案 1 :(得分:1)
TypeScript目前不基于控制流分析进行这种扩展(据我所知)。 这可能是一个很好的功能。
现在,虽然有点涉及,您可以使用typeguard。
function hasMidddle(person: Person): person is { midddle: string } {
return !!person.midddle // btw your check will pass with empty string too.
}
function DoWork(person: Person) {
if (hasMidddle(person)) {
const middleName: string = person.midddle;
const middle: MiddleNamePerson = person
DoStuff(person)
}
}
如果您想使Typeguard更好一点,可以使用ExcludePropType
中的type-plus
:
function hasMidddle(person: Person): person is ExcludePropType<Person, null> {
return !!person.midddle
}
答案 2 :(得分:0)
快速而肮脏的解决方案是只说我知道这是正确的类型,然后将其强制转换为目标接口。
const middle : MiddleNamePerson = person as MiddleNamePerson;
推荐的解决方案是使用Type Guard。使用后,该类型将自动识别为缩小的类型。这是使用类型防护的代码:
export interface Person {
midddle: string | null;
}
export interface MiddleNamePerson {
midddle: string;
}
// type guard function
function isMiddleNamePerson(person: Person): person is MiddleNamePerson {
return person != null && person.midddle != null;
}
export function DoWork(person: Person) {
if (isMiddleNamePerson(person)) {
DoStuff(person); // person is automaticaly recognized as MiddleNamePerson becuse of the type guard
}
}
function DoStuff(value: {midddle: string}) { }