I'm trying to build method generic in typescript and make it returns generic type but TS shows an error on it
export type DataGenerator<T> = <T>(props: {name: string}) => T;
const dataGenerator: DataGenerator<{city?: string}> = ({ name }) => {
return {
city: `${name}`
}
}
dataGenerator({name: "xxx"})
error:
Type '<T>({ name }: { name: string; }) => { city: string; }' is not assignable to type 'DataGenerator<{ city?: string | undefined; }>'.
Type '{ city: string; }' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to '{ city: string; }'.
Any help?
I'm trying to build method generic in typescript and make it returns generic type but TS shows an error on it
export type DataGenerator<T> = <T>(props: {name: string}) => T;
const dataGenerator: DataGenerator<{city?: string}> = ({ name }) => {
return {
city: `${name}`
}
}
dataGenerator({name: "xxx"})
error:
Type '<T>({ name }: { name: string; }) => { city: string; }' is not assignable to type 'DataGenerator<{ city?: string | undefined; }>'.
Type '{ city: string; }' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to '{ city: string; }'.
Any help?
Share Improve this question asked Apr 12, 2023 at 23:05 SuhaibSuhaib 271 silver badge6 bronze badges 9-
1
Is this just a typo? You’ve declared two type parameters of the same name; why? Presumably you mean
type DataGenerator<T> =(props: {name: string}) => T;
but if so then there’s nothing wrong anymore – jcalz Commented Apr 12, 2023 at 23:14 - It's working with the solution but it doesn't show an error if returns a different object like add extra key with city – Suhaib Commented Apr 12, 2023 at 23:56
-
That's a followup question and out of scope for the question as asked... and most likely a duplicate of this. So what should we do here... close the question as cause by a typo? Or should I write up an explanation for why you need to remove the second
<T>
for it to work? – jcalz Commented Apr 13, 2023 at 0:49 -
export type DataGenerator<T> = (props: {name: string}) => T; const dataGenerator: DataGenerator<{city?: string}> = ({ name })=> { return { city: "", postCode: "aaa", } } dataGenerator({name: "xxx"})
– Suhaib Commented Apr 13, 2023 at 0:55 - why accept "postCode" here? as you see the object should has city only – Suhaib Commented Apr 13, 2023 at 0:56
1 Answer
Reset to default 8There are two different kinds of generics in TypeScript.
On the one hand there are generic types, where the generic type parameter is declared on the name of the type:
type GenTypeAlias<T> = T | number;
interface GenInterface<T> { prop: T }
class GenClass<T> { prop: T[] = [] }
where in order to talk about a value of that type you need to specify the type parameter with a type argument:
type SpecificTypeAlias = GenTypeAlias<string>; // okay
interface SpecificInterface extends GenInterface<number> { }; // okay
class SpecificClass extends GenClass<boolean> { }; // okay
type Oops = GenTypeAlias; // error!
// -------> ~~~~~~~~~~~~
// Generic type 'GenTypeAlias' requires 1 type argument.
For generic types, type arguments are specified by the person supplying the value of that type; if the type is a function, the implementer specifies the type argument.
On the other hand, there are generic functions, where the generic type parameter is declared on the call signature:
declare function genFunc<T>(val: T): T[];
type GenFunction = <T>(val: T) => T[];
where you can talk about a value of that type without specifying the type parameter; instead, you only specify the type parameter with a type argument when you call the function:
const genFunc2 = genFunc; // don't specify here
declare const alsoGenFunc: GenFunction; // don't specify here
const strArr = genFunc<string>("abc"); // specify here
// const strArr: string[]
const numArr = genFunc<number>(123); // specify here
// const numArr: number[]
Note that much of the time the piler can infer the type argument for a generic function call, but it's still being specified at call time:
const boolArr = genFunc(true);
// function genFunc<boolean>(val: boolean): boolean[]
// const boolArr: boolean[]
For generic functions, type arguments are specified by the person consuming the value of that type; the caller specifies the type argument.
So let's look at your DataGenerator
definition:
type DataGenerator<T> = <T>(props: { name: string }) => T;
// -------------> ^^^
// 'T' is declared but its value is never read.
Hmm, this seems to be both a generic function and a generic type, and furthermore the same type parameter name T
is used for both of them. Depending on your IDE you might see that the first T
is grayed out, with a message that it has been declared but unused. The inner T
is shadowing or masking the outer one, so the outer one is essentially ignored:
type DataGenerator<T> = <T>(props: { name: string }) => T;
// ^-