Home > Mobile >  Assign value only when key is a valid property of a type in TypeScript
Assign value only when key is a valid property of a type in TypeScript

Time:05-15

I have a type, e.g.:

type Output = {
    fieldA?: string;
    fieldB?: string;
}

And I have an object, e.g.:

const input = { fieldA: "A", fieldB: "B", other: "C" };

Now I want to map the value to another object of type Output, so I want to loop through the object keys and assign only when the key is a valid property of type Output, e.g.:

let output: Output = {};
Object.keys(input).forEach(k => {
    if (k is keyof Output) {
        output[k] = input[k];
    }
});

How do you achieve the above?

CodePudding user response:

TypeScript types are compiled away and not available at runtime, so you can't do this directly. However, you can define the set of valid keys as a type, then use that type to define Output as an object whose keys are that type.

type Field = 'fieldA' | 'fieldB';

type Output = Partial<Record<Field, string>>;

const input = { fieldA: "A", fieldB: "B", other: "C" };

const fields = new Set<Field>(['fieldA', 'fieldB']);

const output = Object
    .keys(input)
    .filter((k): k is Field => fields.has(k))
    .reduce(
        (output, k) => ({ ...output, [k]: input[k] }),
        {} as Output
    );

Notice the type predicate in filter: k is Field. It's saying that when the filter condition is true (i.e. fields.has(k)), then k is a Field. This means that the k in reduce will be of type field instead of string.

CodePudding user response:

I would honestly advise you to initialize output with some properties defined in Output type. So I would approach your scenario this way:

interface Output{
    fieldA?: string;
    fieldB?: string;
}

let input = {
    fieldA : "One",
    fieldB : "Two",
    fieldC : "Three"
};

let output: Output = {
    fieldA: null,
    fieldB: null
};

Object.keys(output).forEach((key)=>
{
    output[key] = input[key];
});

console.log(output );

This way output will hold the properties. Imagine a case where input has not properties that exist in Output type, they would return undefined. so I would rather create an object that declares those properties even prior to mapping them with the input.

It's also important to note how the forEach loop is only interested in the properties existing inside output of type Output... the rest won't be mapped.

  • Related