-
-
Notifications
You must be signed in to change notification settings - Fork 229
Open
Description
Is your feature request related to a real problem or use-case?
Type guards for objects are done using the in
operator. However it doesn't provide autocompletion and doesn't show an error if the tested property is not part of the object.
interface A {
a?: number;
}
interface B {
b: number;
}
const obj = { a: 1 } as A | B;
if ("invalid" in obj) {
console.log(obj.invalid); // invalid is typed as "unknown"
}
Describe a solution including usage in code example
I propose to add two functions, I provide the implementation I'm using in personal projects.
type KeysOf<T> = T extends T ? keyof T : never;
type ExtractByKey<T, K extends KeysOf<T>> = Extract<T, { [P in K]?: any }>;
export function hasProperty<T extends object, K extends KeysOf<T>>(
obj: T,
key: K,
): obj is ExtractByKey<T, K> {
return key in obj;
}
if (hasProperty(obj, "a") { // autocompletion is provided
console.log(obj.a); // `a` is `number | undefined` because it's optional
}
if (hasProperty(obj, "invalid") { // type error here
console.log(obj.invalid);
}
type ExtractByKeyAndAssertNonUndefined<T, K extends KeysOf<T>> =
& ExtractByKey<T, K>
& { [P in K]: Exclude<T[P], undefined> };
export function hasValue<T extends object, K extends KeysOf<T>>(
obj: T,
key: K,
): obj is ExtractByKeyAndAssertNonUndefined<T, K> {
return key in obj && obj[key] !== undefined && obj[key] !== null;
}
if (hasValue(obj, "a") {
console.log(obj.a); // `a` is `number`
}
Who does this impact? Who is this for?
TypeScript users
Metadata
Metadata
Assignees
Labels
No labels