said-m/ts-has-property

Checking additional properties of an object

Opened this issue · 1 comments

I have the following code that fails to build:

import hasProperty from 'ts-has-property';

interface IListOfA {
    type: 'ListOfA';
    a: {
        [id: string]: string
    };
}

interface IEntryB {
    type: 'EntryB';
    b: number;
}

function test(data: unknown) {

    if (!hasProperty(data, 'type', 'string')) {
        throw Error('Missing message type');
    }
    const dataType = data.type;
    switch (dataType) {
        case 'ListOfA': {
            if (!hasProperty(data, 'a', 'object')) {
                throw Error('Not really a ListOfA');
            }
            processListOfA(data);
            return;
        }
        case 'EntryB': {
            if (!hasProperty(data, 'b', 'number')) {
                throw Error('Not really EntryB');
            }
            processEntryB(data);
            return;
        }
    }
}

function processListOfA(data: IListOfA) {
    console.log(data.a);
}

function processEntryB(data: IEntryB) {
    console.log(data.b);
}

Which is a big bummer. I do expect the input to be one of those interfaces, but I want to make sure about that at runtime.

I've tried this workaround:

import hasProperty from 'ts-has-property';
import { ObjectInterface } from '@said-m/common/dist/interfaces';

interface IListOfA {
    type: 'ListOfA';
    a: {
        [id: string]: string
    };
}

interface IEntryB {
    type: 'EntryB';
    b: number;
}

function test(data: unknown) {

    if (!hasProperty(data, 'type', 'string')) {
        throw Error('Missing message type');
    }
    const dataType = data.type;
    switch (dataType) {
        case 'ListOfA': {
            if (!hasProperty<(typeof data) & {a?: object}, keyof Extract<(typeof data) & {a?: object}, ObjectInterface>>(data, 'a', 'object')) {
                throw Error('Not really a ListOfA');
            }
            processListOfA(data);
            return;
        }
        case 'EntryB': {
            if (!hasProperty<(typeof data) & {b?: number}, keyof Extract<(typeof data) & {b?: number}, ObjectInterface>>(data, 'b', 'number')) {
                throw Error('Not really EntryB');
            }
            processEntryB(data);
            return;
        }
    }
}

function processListOfA(data: IListOfA) {
    console.log(data.a);
}

function processEntryB(data: IEntryB) {
    console.log(data.b);
}

Which does build, but is beyond cumbersome... If I had to do this for every future type, and every possible property of that type, I might as well just bite the bullet, and blindly cast based on the type.

Any idea on how to deal with this case more elegantly? That is, the case of one property being required to check for the presence/absence of another property.

hi!, thx for informing about the issue!
you are right, it breaks when two properties are checked sequentially.
i need to check type overloads and a time to research.
for now you can try to install version 2.0.0 (yarn add ts-has-property@2.0.0) if it possible, maybe this will fix the problem.