1

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

1 Answer 1

0

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

Sign up to request clarification or add additional context in comments.

1 Comment

Wow. i am trying understand and first and second variant. And this is hard for me yet.)) But it is working and this very good. Thank for you help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.