Home > Mobile >  How to correctly use types in universal function?
How to correctly use types in universal function?

Time:04-29

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 .

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

CodePudding user response:

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[].

  • Related