Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to correctly use types in universal function?

I have a problem with typescript when i use my universal function

export const createNewArray = <T>(
    arr: T[],
    keyId: keyof T,
    keyTitle: keyof T,
) : [] | TValue[] => {

    const arrayAfterMap = arr.map((item) => ({name: item[keyTitle], id: item[keyId]}));
    
    if(arrayAfterMap.every(item => (typeof item?.id === "string" || typeof item?.id === "number" ) && (typeof item?.name === "string"))) {
        console.log(arrayAfterMap);
        return arrayAfterMap
    }

    return []

};

This function takes array and return new array. Just zero array or array with objects TValue

export  type TValue = {
    name: string
    id: string | number
}

but i get typescript error – TS2322 .

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

TS2322: Type '{ name: T[keyof T]; id: T[keyof T]; }[]' is not assignable to type '[] | TValue[]'.   
Type '{ name: T[keyof T]; id: T[keyof T]; }[]' is not assignable to type 'TValue[]'.     
Type '{ name: T[keyof T]; id: T[keyof T]; }' is not assignable to type 'TValue'.       
Types of property 'name' are incompatible.         
Type 'T[keyof T]' is not assignable to type 'string'.           
Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'string'.             
Type 'T[string]' is not assignable to type 'string'.

In string – " return arrayAfterMap "

I can`t understand what i do wrong. I made a check for compliance with TValue. if the check passed, then I return the array after map , and if not, then empty. But it is not work. I will happy any advice!

And I made a test application on codesandbox so that you can see the code live. But codesandbox doesn’t display this error right away – link

>Solution :

The easiest way to solve this might be a type guard:

const isTValueArray = (arr: any[]): arr is TValue[] => arr.every(item => 
  (typeof item?.id === "string" 
    || typeof item?.id === "number" ) 
    && (typeof item?.name === "string")
  )

After invoking the function you can return arrayAfterMap with the correct type:

if(isTValueArray(arrayAfterMap)) {
  return arrayAfterMap
}

A more complex approach would be to add two more generic types for each key to the function:

export const createNewArray = <
  T extends {[keyId in KeyId]: string}  // T[KeyId] should be string
    & {[keyTitle in KeyTitle]: string | number},
  KeyId extends keyof T, 
  KeyTitle extends keyof T>(
    arr: T[],
    keyId: KeyId,
    keyTitle: KeyTitle,
) : TValue[] => {

    const arrayAfterMap = arr.map((item) => ({name: item[keyTitle], id: item[keyId]}));
    
    return arrayAfterMap
};

Here TypeScript will automatically know that arrayAfterMap is equivalent to TValue[].

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading