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