ts中已使用 in 来判断对象的属性后,获取值还是报错。怎么处理?

新手上路,请多包涵
// 对于 unknown 类型怎么获取其属性值
function getValue(data: unknown) {
  // 为什么已经判断 "value" in data,还是会报错
  if (typeof data === 'object' && data && 'value' in data) {
    return data.value; // error 类型“object”上不存在属性“a”
  }
  return '';
}

data 类似后端返回的值,这里使用 unknown。上述代码应该怎么改,才能正常获取data属性值,求各位大佬帮忙看看

阅读 5.9k
2 个回答

TypeScript 内置的类型断言有限,但提供了类型断言函数来扩展类型断言。从 Runtime 来看,类型断言函数返回的是一个布尔值,但是从 Compile Time 来看,它可以断言参数是否某一特定类型(声明形式,这个形式表明充值信任 Runtime 的判断结果):

示例:

// 定义类型断言函数,断言参数含 value 属性
function hasValue(it: unknown): it is ({ value: unknown }) {
    return typeof it === "object" && it !== null && "value" in it;
}

// 先断言(编译器会根据断言识别类型),再取值
function getValue(data: unknown): unknown {
    return hasValue(data) ? data.value : "";
}

hasValue(it) 会充分信任 Runtime 对 it 的判断,如果返回 true,就认为 it{ value: unknown } 类型(或其子类型)。所以在 getValue() 中,条件表达式为真的情况下,可以识别 data 为含有 value 属性的类型。

注意这里的细节,value 属性是声明为 unknown 类型的。这是基于我们的断言函数里没有去判断 value 的类型,只是判断了有没有这个属性。如果期望它是 string 类型的,可以修改如下:

function hasValue(it: unknown): it is ({ value: string }) {
//                                              ^^^^^^ unknown ⇒ string
    return typeof it === "object" && it !== null && "value" in it;
}

hasValue() 在没有修改内部逻辑的情况下,声明断言 valuestring 类型的。而基于编译器对类型断言的“充分信任”(因为编译器确实对 Runtime 无能为力,只能选择信任声明),getValue() 可以将返回类型修改为 string

function getValue(data: unknown): string {
    return hasValue(data) ? data.value : "";
}

条件表达式中,两个分支的类型都是 string,所以这里不会有问题。但实际上调用的时候,如果给 getValue() 一个含有非 string 类型 value 属性的对象,是可能产生运行时错误的。比如:

const s = getValue({ value: new Date() });   // 这句话不会有问题,编译期也不会出错
const ss = s.substring(0, 1);         // 编译期不会有问题,但是运行时会出错

上面示例,基于类型声明,sstring 类型的。但它实际不是 string 类型,所以调用 substring 会出错。

严格一点的 hasStringValue() 应该是这样(hasValue 的逻辑可以合并到 hasStringValue 中来减少一个函数):

function hasValue(it: unknown): it is ({ value: string }) {
    return typeof it === "object" && it !== null && "value" in it;
}

function hasStringValue(it: unknown): it is ({ value: string }) {
    return hasValue(it) && typeof it.value === "string";
}
function getValue(data: unknown): string {
    return hasStringValue(data) ? data.value : "";
}

const s = getValue({ value: new Date() });

这时候的 s 就会得到 "",也就是表达式的 else 分支的值。

sing-type-predicates

type ObjectA = {
  value: string;
};
const isObjectA = (data: unknown): data is ObjectA => {
  return typeof data === 'object' && data !== null && 'value' in data;
};
// 对于 unknown 类型怎么获取其属性值
function getValue(data: unknown) {
  // 为什么已经判断 "value" in data,还是会报错
  if (isObjectA(data)) {
    return data.value; // error 类型“object”上不存在属性“a”
  }
  return '';
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
1 篇内容引用
推荐问题