'Angular EventEmitter with different types

I’m building a checkbox component that can manage three states depending on if a prop is passed.

@Input() value: boolean|null = false;
@Input() triState = false;

@Output() valueChange = new EventEmitter<boolean|null>();

So when I want to manage a 3-state value it works, I pass in a variable of type boolean|null and no problem.

However, if I just want a normal two-state checkbox, I still have to pass in a variable of boolean|null type, which I find annoying for the client code. Passing a boolean value gives me the following error:

(For the value prop)
Type ‘boolean|null’ is not assignable to type ‘boolean’. Type ‘null’ is not assignable to type ‘boolean’.

I know that this is because my EventEmitter emits boolean|null. So what would be the best way to solve this? Is there a way to emit only a boolean if triState is false, and boolean or null if it’s true?



Solution 1:[1]

I see two possible options:

  1. Using types for the EventEmitter generics:

    export type TriState = boolean | null;
    export type DualState = boolean;
    
    @Output() stateChange = new EventEmitter<DualState|TriState>();
    
  2. Creating two separate event emitters:

    @Output() dualStateChange = new EventEmitter<boolean>();
    @Output() triStateChange = new EventEmitter<boolean|null>();
    
    • if triState is true, you would emit from triStateChange, else from dualStateChange.

Solution 2:[2]

Easiest (but most errorprone) way is to just reinterpret_cast / std::memcpy if the strings have fixed length:

// no padding
#pragma pack(push, 1)
struct foo {
 char       str1[12];
 uint16_t   int1;
 char       str2[3];
 uint32_t   int2;
 char       str3[4];   
 };
#pragma pack(pop)

void func(const byte* data, const size_t len) {
    assert(len == sizeof(foo));

    // non owning
    const foo* reinterpreted = reinterpret_cast<const foo*>(data);

    // owning
    foo reinterpreted_val = *reinterpret_cast<const foo*>(data);
   
    foo copied;
    memcpy(&copied, data, len); 
}

Notes:

Slightly better approach:

struct foo {
 char       str1[13];
 uint16_t   int1;
 char       str2[4];
 uint32_t   int2;
 char       str3[5];   
 };

void func(const char* data, const size_t len) {
    foo f;

    memcpy(f.str1, data, 12);
    f.str1[12] = '\0';
    data+=12;

    memcpy(&f.int1, data, sizeof(uint16_t));
    data+=sizeof(uint16_t);

    memcpy(f.str2, data, 3);
    f.str2[3] = '\0';
    data+=3;

    memcpy(&f.int2, data, sizeof(uint32_t));
    data+=sizeof(uint32_t);

    memcpy(f.str3, data, 4);
    f.str3[4] = '\0';
    data+=4;
}

Notes:

  • You could combine both approaches to get rid of the pointer arithmetic. That would also account for any padding in your struct you might have.

Solution 3:[3]

  1. I think the easiest way to do this is to change the string inside the structure to the type of char. Then you can easily copy the objects of this structure according to its size.
  2. you will have to somehow deal with the byte order on machines with different byte order
struct foo {
    char                str1[12];
    uint16_t              int1;
    char                str2[3];
    uint32_t              int2;
    char                str3[5];
};

byte* Encode(foo* p, int Size) {
    int FullSize = Size * sizeof(foo);
    byte* Returner = new byte[FullSize];

    memcpy_s(Returner, FullSize, p, FullSize);

    return Returner;
}

foo * func(const byte* data, const size_t len) {
    int ArrSize = len/sizeof(foo);

    if (!ArrSize || (ArrSize* sizeof(foo)!= len))
        return nullptr;

    foo* Returner = new foo[ArrSize];

    memcpy_s(Returner, len, data, len);

    return Returner;
}

int main()
{
    const size_t ArrSize = 3;
    foo Test[ArrSize] = { {"Test1",1000,"TT",2000,"cccc"},{"Test2",1001,"YY",2001,"vvvv"},{"Test1",1002,"UU",2002,"bbbb"}};
    foo* Test1 = nullptr;

    byte* Data = Encode(Test, ArrSize);

    Test1 = func(Data, ArrSize * sizeof(foo));

    if (Test1 == nullptr) {
        std::cout << "Error extracting data!" << std::endl;
        delete [] Data;
        return -1;
    }

    std::cout << Test1[0].str1 << " " << Test1[1].str1 << " " << Test1[2].str3 << std::endl;

    delete [] Data;
    delete[] Test1;

    return 0;
}

output

Test1 Test2 bbbb

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 CCBet
Solution 2
Solution 3