網站搜尋

javascript - Typescript:當鍵是寬類型時,如何判斷物件屬性是否已定義的類型保護?


我有一個函數可以返回物件屬性是否為 undefined。我需要這個函數,而不是僅僅使用 obj[key] === undefined,因為否則我有時會得到 Property 'foo' does not exist on type 'Bar'.。當屬性鍵是字面量時,寫類型很直接。也就是說:

function hasDefinedProp<
  Obj extends Partial<Record<string, any>>,
  Prop extends string,
>(
  obj: Obj,
  prop: Prop,
): obj is Obj & Record<Prop, Prop extends keyof Obj ? Exclude<Obj[Prop], undefined> : unknown> {
  return obj[prop] !== undefined;
}

const obj: Partial<Record<string, number>> = {};
if (hasDefinedProp(obj, 'foo')) {
    obj.foo + 1; // No error.
    obj.bar + 1; // "Object is possibly 'undefined'."
}

然而,當鍵的類型是寬類型時,這就不起作用,即:

const obj: Partial<Record<string, number>> = {};
const key: string = '';
if (hasDefinedProp(obj, key)) {
    obj[key] + 1; // No error.
    obj.bar + 1; // No error. Should be "Object is possibly 'undefined'."
}

有可能讓類型保護對廣泛類型起作用嗎?

答案#1

據我所知,這是不可能的。一旦你在 const key: string=''; 中明確添加了 string 類型——TS 就無法縮小 key 的字面類型。結果是,你可以使用任何你想要的字符串來訪問屬性。在這個例子中,TS 無法區分兩個類型都是 string 的情況:

const obj: Partial<Record<string, number>> = {};
const key: string = '';
if (hasDefinedProp(obj, key)) {
    obj[key] + 1; // No error.
    obj.bar + 1; // No error. Should be "Object is possibly 'undefined'."
}

這讓我想起一個有明確類型和不可變類型斷言的例子:

const record: Record<string, number> = {
    a: 1
} as const;

type Keys = keyof typeof record // string instead of "a"

這意味著在這種特定情況下,禁止使用寬類型可能是一個好主意:

type ForbidWide<Prop> = Prop extends string ? string extends Prop ? never : Prop : never


function hasDefinedProp<
    Obj extends Partial<Record<string, any>>,
    Prop extends string,
    >(
        obj: Obj,
        prop: ForbidWide<Prop>,
): obj is Obj & Record<Prop, Prop extends keyof Obj ? Exclude<Obj[Prop], undefined> : unknown> {
    return obj[prop] !== undefined;
}


const obj: Partial<Record<string, number>> = {};
const key: string = '';
hasDefinedProp(obj, key) // error

遊樂場