1

Say I have an array of objects and of the keys is called render which can be an optional function that takes a parameter (of which the type is unknown)

const array = [{a: 1}, {b: 2, render: renderFunction}, {c: 3, render: anotherFunction}]

say the 2nd object renderFunction takes a number as a param and anotherFunction takes a string as the param, how do I achieve that with generics?

const array: ArrayType<unknown> = [
  {a: 1},
  {b: 2, render: renderFunction} as MyType<number>,
  {c: 3, render: anotherFunction} as MyType<string>
]

but this doesn't work Type 'unknown' is not assignable to type 'number'

any ideas?

2 Answers 2

1

I am assuming you have only one key 'a' other than render in Object.

You can define types like so:

interface ArrayItem {
    a: number;
    render?: <Type extends unknown>(arg: Type) => void;
}

const func1 = <Type extends unknown>(value: Type) => { };
const func2 = <Type extends unknown>(value: Type) => { };

const data: Array<ArrayItem> = [
    { a: 1 },
    { a: 2, render: func1 },
    { a: 3, render: func2 }
];

data[1].render?.<string>("test");
data[2].render?.<number>(23);

You can use above code for generics but to restrict parameter types you have to define custom type instead of unknow and extend it.

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

2 Comments

that's not possible unfortunately. I should have explained better. param. could literally be anything and I don't want to clutter that function with types I've created myself e.g. param: string | number | myType | anotherOfMyTypes. I want to use generics so that I can just tell it the type beforehand
I just updated my answer according to your needs. Let me know if this works fine with you.
0

I think that the best would be let TypeScript infer the type, just define ArrayItem as generic:

interface ArrayItem<T> {
    a: number;
    render?: (param: T) => void;
}

Then the parameter may be of any type:

const func1 = (value: string) => { };
const func2 = (value: number) => { };

And we remove the type definition from the assignment, in this way TypeScript will infer it:

const data = [
    { a: 1 } as ArrayItem<never>,
    { a: 2, render: func1 } as ArrayItem<string>,
    { a: 3, render: func2 } as ArrayItem<number>
] as const;

I added as const at the end in this way you'll tell TypeScript that each index of the array has a specific type, in this way the inferred type will be [ArrayItem<never>, ArrayItem<string>, ArrayItem<number>] and not ArrayItem<string>[] | ArrayItem<number>[], which is more desirable.

In the end, you can just use it like:

data[1].render?.("test");
data[2].render?.(23);

And TypeScript will advise you the correct type for each element.

The answer was based on the one provided by @OsamaMalik

6 Comments

Hint: if you remove the as const you'll have to assert the type for each index
this looks good only issue I get now. is: The type 'readonly .... ' is 'readonly' and cannot be assigned to the mutable type 'ArrayItem<unknown>'
Are you trying to use .push? You have to consider the object immutable and recreate the array each time you need to add an element. Otherwise you can remove as const but in this way you lose the type suggestions
Anyway your request is pretty complex, TypeScript tries its best but if you want it to "remember" the type for each index you have to provide immutability, otherwise any index may assume any type at any time :) just think what would happen if you do data.sort(), TypeScript cannot know it
nope not using push. just defined an array of objects and each object has the key render and I just want to define the type for that
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.