Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript - key/property type guard

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.

like image 616
user3690467 Avatar asked Oct 03 '19 08:10

user3690467


People also ask

What is type guard in TypeScript?

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.

What is type keyword in TypeScript?

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.

How do I compare types in TypeScript?

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.

What is type assertion in TypeScript?

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.


2 Answers

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

like image 168
Linda Paiste Avatar answered Sep 21 '22 00:09

Linda Paiste


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>

like image 36
apokryfos Avatar answered Sep 20 '22 00:09

apokryfos