Can I create a typeguard which asserts that a particular property exists (or has a specific type) in an object.
I.e
I have an interface Foo
:
interface Foo {
bar: string;
baz: number;
buzz?: string;
}
Now an object of type Foo
will have an optional property buzz. How would I write a function which asserts that buzz exists:
i.e
const item: Foo = getFooFromSomewhere();
if (!hasBuzz(item)) return;
const str: string = item.buzz;
How would I implement hasBuzz()
?. Something along the lines of a typeguard:
function hasBuzz(item: Foo): item.buzz is string {
return typeof item.buzz === 'string'
}
Does something like this exist?
PS: I understand I can just do:
const item = getFooFromSomewhere();
if (typeof item.buzz === 'string') return;
const str: string = item.buzz;
But my actual use-case requires me to have a separate function which asserts the existence of buzz
.
A type guard is a TypeScript technique used to get information about the type of a variable, usually within a conditional block. Type guards are regular functions that return a boolean, taking a type and telling TypeScript if it can be narrowed down to something more specific.
Type keyword in typescript: In typescript the type keyword defines an alias to a type. We can also use the type keyword to define user defined types.
In Typescript, we have three ways to work with it using: typeof: the keyword helps to check values types, like boolean, string, number, etc. instanceof: the keyword to compare the object instance with a class constructor. type guards: The powerful way to check types using typescript feature language.
In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.
I don't like the existing answers here because they are all specific to checking a Foo
object, but you can define a hasBuzz
typeguard that will check any object to see if it has a buzz
property.
interface Buzzable {
buzz: string;
}
function hasBuzz<T extends {buzz?: any}>(obj: T): obj is T & Buzzable {
return typeof obj.buzz === "string";
}
By using a generic T
for the input and returning obj is T & Buzzable
rather than just obj is Buzzable
you won't lose any information about specific interfaces such as Foo
when checking with hasBuzz
.
If hasBuzz(item: Foo)
is true
, then typescript knows that the type of item
is Foo & Buzzable
. In this case that is the same as Required<Foo>
since Foo
has an optional buzz
property, but you can check any object. hasBuzz({})
is perfectly valid, and should always return false
.
Typescript Playground Link
The point of a guard is to allow you to narrow down when you're not sure what the type is:
What might work in your case is:
interface Foo {
bar: string;
baz: number;
buzz?: string;
}
function hasBuzz(item: Foo|Required<Foo>): item is Required<Foo> {
return typeof item.buzz === 'string'
}
const f : Foo = {
bar: 'a',
baz: 1,
buzz: 'x'
};
const str : string = f.buzz; // error
if (hasBuzz(f)) {
const str2 : string = f.buzz; // works
}
Required
is a helper type that given another type will return that type with all properties required (available since ts 2.8). This will narrow down your item
variable as being of type Required<Foo>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With