echo
echo

Reputation: 51

Narrowing Union Type of Set and Array for find method

I have the following Data model

Daily Data of a person

interface FetchDocument {
    Date : number,
    Hours : number,
}   

A collection of those data

type Data_Array = Set<FetchDocument> | FetchDocument[]

interface GroupDocument {
    name : string,
    Data : Data_Array,
   
}

A sample data for test is as follows

let fetchArray : GroupDocument[] = [
    {
        name : 'John',
        Data : [
            {
                Date : 13,
                'Hours' : 14
            },
            {
                Date : 12,
                'Hours' : 433
            }
        ] 
    }
]

When I want to find on that sample using find method

for(let i= 0; i < fetchArray.length ; i++){
    let obj = fetchArray[i].Data.find((obj : FetchDocument) => obj.Date ===  13 )
}

The compiler is complaining as below because of Set Type on Data_Array.

error TS2339: Property 'find' does not exist on type 'Data_Array'.

Property 'find' does not exist on type 'Set'.

So what I have tried is try to narrow or re-assign to solve in three ways

Narrowing

 if(Array.isArray(fetchArray[i].Data)){
        let obj = fetchArray[i].Data.find((obj : FetchDocument) => obj.Date ===  13 )
 }
 if(typeof(fetchArray[i].Data.find) === 'function'){
        let obj = fetchArray[i].Data.find((obj : FetchDocument) => obj.Date ===  13 )
    }
   if(fetchArray[i].Data instanceof Array){
        let obj = fetchArray[i].Data.find((obj : FetchDocument) => obj.Date ===  13 )
    }

Re-assigning to array

fetchArray[i].Data = [...fetchArray[i].Data] 
let obj = fetchArray[i].Data.find((obj : FetchDocument) => obj.Date ===  13 )
fetchArray[i].Data = Array.from(fetchArray[i].Data.values())
let obj = fetchArray[i].Data.find((obj : FetchDocument) => obj.Date ===  13 )

But the error can't be solved so what can I do to resolve it.

Playground link : Link

Set is used when fetching data to detect duplicate data so changing it will cause for changing code on other part of codebase

Upvotes: 0

Views: 229

Answers (2)

Zain_Ul_Din
Zain_Ul_Din

Reputation: 27

You probably need to use Type Assertions in this case, but if the data is not an array, it will cause errors during runtime that's why you should also be careful about runtime errors.

Here is how you can do this,


for(let i= 0; i < fetchArray.length ; i++){
    // treat data as a FetchDocument[] type note! it can be another type on run time
    const data = fetchArray[i].Data as FetchDocument[] 
    // consider use error handling here
    let obj = data.find((obj : FetchDocument) => obj.Date ===  13 )
}

Demo

Updated

You can also use Array.from method without adding any extra if

for(let i= 0; i < fetchArray.length ; i++){
    let obj = Array.from (fetchArray[i].Data)
     .find((obj : FetchDocument) => obj.Date ===  13 )
}

Upvotes: 1

Rodrigo Novais
Rodrigo Novais

Reputation: 115

This is due what we call Temporal Uncertainty. Typescript doesn't know if there's any other codes changing the content of fetchArray that could lead to a different value while executing the code.

There is no need to explicit type assertion whatsoever, just save the value you wanna check in another variable as soon as you validate it

for (let i = 0; i < fetchArray.length; i++){
    let arr = fetchArray[i].Data;
    if (arr instanceof Array) {
        let obj = arr.find((obj : FetchDocument) => obj.Date === 13)
    }
}

Upvotes: 2

Related Questions