I've got two functions which do exactly the same thing so it seems like a prime candidate for a generic function to replace them. However I can't quite get it right, the extracted value isn't exactly the same as the original type as the return type should be the original result/error type but now without undefined.
type PurchaseResult = {
result: Value || undefined,
error: Error || undefined,
}
One of the existing examples of the function (for the other function replace key and type with result/value:
const extractErrors = (
purchaseResults: PurchaseResult[]
) =>
purchaseResults
.map(({ error }) => error)
.filter((error): error is Error => Boolean(error));
Failed Attempt
const extractOnlyDefinedValuesFromArrayOfObjects = <
T,
K extends keyof A,
A extends { [k in K]: T }
>(
list: A[],
key: K
): T[] =>
list
.map((obj) => obj[key])
.filter((value) => typeof value !== 'undefined');
const extractedResults = extractOnlyDefinedValuesFromArrayOfObjects<
Value,
'result',
PurchaseResult
>('result', arrayOfResults);
Thanks!
CodePudding user response:
This syntax Value || undefined
is not allowed, use |
instead.
I don't know whether you have defined type Value
or not, that's why I have added T
generic - for Value
;
Consider this example:
type PurchaseResult<Value> = {
result: Value | undefined,
error: Error | undefined,
}
const extractErrors = <
T,
Key extends keyof PurchaseResult<T>,
Data extends PurchaseResult<T>[]
>(
purchaseResults: Data,
key: Key,
) =>
purchaseResults
.map(elem => elem[key])
.filter((elem): elem is NonNullable<Data[number][Key]> => Boolean(elem));
declare let results: PurchaseResult<number>[]
const result = extractErrors(results, 'result') // number[]
const result2 = extractErrors(results, 'error') // Error[]
I have used Data
just to alias PurchaseResult<T>[]
. Since we have appropriate generic for our list, we can use NonNullable<Data[number][Key]>
for our typeguard
CodePudding user response:
A generalised version that accepts any array of key-value pairs and returns a typed array of values for the specified key, extracted from each record. (Playground)
/**
* extractKey extracts record[key] from an array of records
* @param key - a key in records
* @param records - an array of key-value records of the same type
* @return - `key` for each record, where record[key] is defined.
*/
const extractKey = <T extends Record<string, any>, K extends keyof T>(
key: K,
records: T[]
): Array<NonNullable<T[K]>> => {
return records
.map(el => el[key])
.filter(el => typeof el !== "undefined")
}
// define a type to test on, but can be any key-value pair type
type PurchaseResult = {
result?: number
error?: string
}
// some dummy data
const testMe: PurchaseResult[] = [{result: 1}, {error: "no result!"}, {result: 4}]
// test it!
const results = extractKey('result', testMe) // []number
const errors = extractKey('error', testMe) // []string