使用
in
运算符进行类型缩小是否仅适用于文字,还是我遗漏了某些内容?
请帮助我理解为什么会发生这种情况。
interface A {
a: string;
}
interface B {
b: number;
}
// This narrows the type correctly to A
function test(arg: A | B): string {
if ('a' in arg) {
return arg.a;
}
return arg.b.toFixed(2);
}
// This doesn't
function test2(arg: A | B): string {
let a = 'a';
if (a in arg) {
return arg.a;
}
return arg.b.toFixed(2);
}
对于 n in x 表达式,其中 n 是字符串文字或字符串文字类型,x 是联合类型,“true”分支缩小到具有可选或必需属性 n 的类型,“false”分支缩小到具有可选或必需属性 n 的类型具有可选或缺失的属性 n。
所以,我愿意打赌它只适用于文字
解决方法
interface A {
a: string;
}
interface B {
b: number;
}
// This narrows the type correctly to A
function test(arg: A | B): string {
if ('a' in arg) {
return arg.a;
}
return arg.b.toFixed(2);
}
const isIn = <T, Prop extends string>(obj: T, prop: Prop): obj is T & Record<Prop, unknown> => prop in obj
const isIn2 = <T, Prop extends string>(obj: T, prop: Prop): obj is T & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop)
// This doesn't
function test2(arg: A | B): string {
const a: 'a' = 'a' as 'a';
if (isIn(arg, a) /** OR isIn2(arg, a) */) {
return arg.a; // A
}
return arg.b.toFixed(2);
}
是的,我同意这很奇怪。我有以下代码:
let name = ''
if (characteristic.uuid in peripheralsValueUuidNameMap) {
const uuid =
characteristic.uuid as keyof typeof peripheralsValueUuidNameMap
name = peripheralsValueUuidNameMap[uuid]
}
我会假设 ts 推断出
as keyof typeof peripheralsValueUuidNameMap
但如果我以这种方式强制它,它只会停止抱怨。
我知道
characteristic.uuid
是 peripheralsValueUuidNameMap
的键,因为 in
检查成功。
peripheralsValueUuidNameMap
是一个 const 类型,所以关于它的一切都是已知的。
问题是,所有键都是 const
字符串,因此无法按字符串类型进行索引。奇怪的是,即使in
检查已经完成了。
在这种情况下它确实有效:
if (characteristic.uuid in servCharMap) {
const charData = servCharMap[characteristic.uuid]
}
因为 servCharMap 有类型
type ServCharMap = {
[key: string]: ServChar
}
但这里我们甚至不需要
in
检查,const charData = servCharMap[characteristic.uuid]
单独这段代码不会给出类型错误,因为我们正在使用字符串类型索引 servCharMap
。
所以综上所述,两种情况都有缺陷,都隐藏了枪。