Tengo una función que devuelve si la propiedad de un objeto no está undefined . Necesito esta función en lugar de simplemente hacer obj[key] === undefined porque, de lo contrario, a veces obtendría Property 'foo' does not exist on type 'Bar'. . Es sencillo escribir el tipo cuando la clave de propiedad es un literal. Es decir:
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'." }Sin embargo, esto no funciona cuando el tipo de clave es de tipo ancho, es decir:
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'." }¿Es posible hacer que el tipo de protección funcione para tipos anchos?
AFAIK, no es posible. Una vez que haya agregado un tipo de string explícito a la const key: string = ''; -TS no puede restringir el tipo de key literal. Como resultado, puede usar cualquier cadena que desee para acceder a una propiedad. TS no puede distinguir dos tipos con string de tipo en este ejemplo:
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'." }Esto me recuerda un ejemplo con tipo explícito y aserción de tipo inmutable:
const record: Record<string, number> = { a: 1 } as const; type Keys = keyof typeof record // string instead of "a"Significa que en este caso particular podría ser una buena idea prohibir el uso de tipos anchos:
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