AlexandruC
AlexandruC

Reputation: 3637

Typescript Interface with nullable properties

I have an interface IFilterSet with nullable fields:

export interface IFilterSet {
        filter1?: boolean;
        filter2?: boolean;
        filter3?: number;
        fitler4?: string;
    }

I receive another object X, that may contain IFilterSet object among others.

I want to map the IFilterSet properties of object X. First quess would be to make a loop for those properties, but because they are nullable, it won't work, there will be nothing to loop through. Initialiazing an empty IFilterSet would defeat the purpose of those properties being nullable, right?

Upvotes: 2

Views: 6056

Answers (3)

Raphaël
Raphaël

Reputation: 3726

Defining optional properties like filter1? does not means that the value will be defined or null. It means that the property may be defined (or not).

Let say you receive the following object:

{
   "name": "foo",
   "type": "bar"
}

You can use it like this:

const filterSet = objectX as IFilterSet;

if (filterSet.filter1 === true) {
    console.log('Do something when filter1 is defined and true');
}
else if (filterSet.filter1 === false) {
    console.log('Do something when filter1 is defined and false');
}
else {
    console.log('filter1 is not true nor false (may be null, undefined or something else)');
}

const value2 = filterSet.filter2 || false;
const value3 = filterSet.filter3 || 0;
console.log('value2 and value3 will always have a value');

You don't really need to use null for those cases.

If you really need to isolate the properties of IFilterSet in a different object. I would recommend using a function like this one:

const filterSetFrom = (source: any) => {
    const filterSet = {};
    ['filter1', 'filter2', 'filter3', 'filter4']
        .forEach((property) => filterSet[property] = source[property] || null);
    return filterSet;
}; 

Upvotes: 1

Justinas Marozas
Justinas Marozas

Reputation: 2682

To have properties of IFilterSet as as properties of X, all you need to do is declare that X extends IFilterSet, or use type assertions if you can't control Xs declaration.

class X implements IFilterSet {
    ...
}
...
const x:X = ...
const filterSet = x as IFilterSet;

If you're making properties of IFilterSet optional to obey lose structure of X, you could have IFilterSet with required properties (without ?) and have X implement Partial<IFilterSet>. Partial<T> gives you a type with all properties of T, but made optional.

Upvotes: 0

Wickoo
Wickoo

Reputation: 7345

I'm not sure if I fully get your question, but how about this:

function map(filterSet: IFilterSet, obj: X) {
    (obj as any)['filter1'] = filterSet.filter1;
    (obj as any)['filter2'] = filterSet.filter2;
    (obj as any)['filter3'] = filterSet.filter3;
    (obj as any)['filter4'] = filterSet.filter4;
}

This way, the X instance will get all the properties of filterSet and gets undefined for the ones are not present, but all the fields will end up there.

Or this (if you try to construct a new IFilterSet instance):

function map(obj: X): IFilterSet {
    return {
        filter1: (obj as any)['filter1'],
        filter2: (obj as any)['filter2'],
        filter3: (obj as any)['filter3'],
        filter4: (obj as any)['filter4']
    }
}

Here you will get an instance of IFilterSet, with their fields either undefinedor the value of the filter1 through filter4.

Either way you HAVE TO hardcode the name of fields, I'm not sure if you can get it differently.

Upvotes: 1

Related Questions