Typescript does not trust that a property will definitely be created in an object

Viewed 98

I have the following Typescript code which I'm trying to fit the type Data with at least a mainData property.

interface Data {
    [K: string]: string
    mainData: string
}

function getDataObj(dynamicData: string[]): Data {
    return dynamicData.reduce((res, val) => ({
        ...res,
        [val]: 'some stuff'
    }), {})
}

// In the actual app, the dynamicData are coming dynamically and are not hardcoded like so in this example
const dynamicData = ['dataA', 'dataB', 'mainData']

const dataObj: Data = getDataObj(dynamicData)

However, the code above will give me the following error:

Property 'mainData' is missing in type '{}' but required in type 'Data'.(2741)
input.ts(3, 5): 'mainData' is declared here

The problem is that Typescript doesn't trust that mainData will definitely exist in the dynamicData parameter array. However, in my case, the mainData property will definitely exist on the array but I just want to add it to the Data interface for better code completion.

Here's the snippet on TS Playground.

What else can I do so that Typescript will trust that I will have mainData created as one the properties in the Data object?

2 Answers

try this way, in the reduce function add {mainData: ''} as initial value.

interface Data {
    [K: string]: string;
    mainData: string;

}

function getDataObj(dynamicData: string[]): Data {
    return dynamicData.reduce((res, val) => ({
        ...res,
        [val]: 'some stuff'
    }), {mainData: ''})
}

// In the actual app, the dynamicData are coming dynamically and are not hardcoded like so in this example
const dynamicData = ['dataA', 'dataB', 'mainData'];

const dataObj: Data = getDataObj(dynamicData);
console.log(dataObj);

It seems like in order for typescript to be able to know that a certain item will be in any array, you have to assert, via a type, that that value will be present. For instance, like so:

interface Data {
    [K: string]: string
    mainData: string
}

const getDataObj_ = <T extends string>(dynamicData: ['mainData', ...T[]]): Data => dynamicData.reduce((p, c) => ({
    ...p,
    [c]: 'some stuff',
} as { [key in T | 'mainData']: string }), {} as { [key in T | 'mainData']: string });

// In the actual app, the dynamicData are coming dynamically and are not hardcoded like so in this example
const dynamicData: ['mainData', ...string[]] = ['mainData', 'dataA', 'dataB']

const dataObj: Data = getDataObj_(dynamicData)
Related