最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Typescript generic: 'T' could be instantiated with an arbitrary type which could be unrelated - Sta

programmeradmin2浏览0评论

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
 |  Show 4 more ments

1 Answer 1

Reset to default 8

There 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;
//                 ^-

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论