Skip to content

add "has_property" and "has_value" type guards for objects #189

@raythurnvoid

Description

@raythurnvoid

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

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions