I want pass two generic condition for pass array type field name but not accepted second condition.
This my method declaration and this no problem.
firstOrDefault<K extends keyof T>(predicate?: (item: T) => boolean, recursiveItem?: K): T;
Above method declaration is working but I want pass only Array type in recurviseItem field.
I'm trying this method declaration but doesn't work.
firstOrDefault<K extends keyof T & T[]>(predicate?: (item: T) => boolean, recursiveItem?: K): T
How can solve this problem?
Sample Code
let departments : IDepartment[] = [
{
name: 'manager',
subDepartments: [
{
name: 'accountant'
}
]
}
]
// This my method declaration and this code worked but you can pass name and subDepartments field pass for recursiveItem parameter but i want only T[] type field pass so only subDepartments.
let department = departments.firstOrDefault(d => d.name == 'accountant', 'subDepartments')
console.log(department)
interface Array<T> {
firstOrDefault<K extends keyof T>(predicate?: (item: T) => boolean, recursiveItem?: K): T;
}
Array.prototype.firstOrDefault = function(this, predicate, recursiveItem) {
if (!predicate)
return this.length ? this[0] : null;
for (var i = 0; i < this.length; i++) {
let item = this[i]
if (predicate(item))
return item
if (recursiveItem) {
let subItems = item[recursiveItem]
if (Array.isArray(subItems)) {
var res = subItems.firstOrDefault(predicate, recursiveItem)
if (res)
return res
}
}
}
return null;
}
interface IDepartment {
name?: string,
subDepartments?: IDepartment[]
}
Try this type definition
type ArrayProperties<T, I> = { [K in keyof T]: T[K] extends Array<I> ? K : never }[keyof T]
class A {
arr1: number[];
arr2: string[];
str: string;
num: number;
func() {}
}
let allArr: ArrayProperties<A, any>; // "arr1" | "arr2"
let numArr: ArrayProperties<A, number>; // "arr1"
so firstOrDefault
would look like that (I put ArrayProperties<T, T>
to restrict recursiveItem only for recursive type i.e. only properties of type IDepartment[]
could be used, however you can put ArrayProperties<T, any>
if you want to accept any array)
function firstOrDefault<T>(predicate?: (item: T) => boolean, recursiveItem?: ArrayProperties<T, T>): T { }
With your example
interface IDepartment {
name: string;
subDepartments?: IDepartment[];
}
let departments : IDepartment[] = [
{
name: 'manager',
subDepartments: [
{
name: 'accountant'
}
]
}
]
let a = firstOrDefault(((d: IDepartment) => d.name === 'accountant'), 'subDepartments'); // OK
let b = firstOrDefault(((d: IDepartment) => d.name === 'accountant'), 'subDepartment'); // error: [ts] Argument of type '"subDepartment"' is not assignable to parameter of type '"subDepartments"'.