Typescript `in` 运算符类型保护仅缩小为文字

Typescript `in` operator type guard narrowing only as literal

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);
}

Regarding to docs

For a n in x expression, where n is a string literal or string literal type and x is a union type, the “true” branch narrows to types which have an optional or required property n, and the “false” branch narrows to types which have an optional or missing property 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);
}