Skip to content

Dynamic generic type inference for object literals #24375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
agyemanjp opened this issue May 24, 2018 · 5 comments
Closed

Dynamic generic type inference for object literals #24375

agyemanjp opened this issue May 24, 2018 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@agyemanjp
Copy link

agyemanjp commented May 24, 2018

Search Terms

generics, inference, objects

Suggestion

I can define a generic function like so:
const fn = <T>(arg: T)=>Partial<T>

In this case, TypeScript can sometimes infer the type parameter of the function based on the actual parameters I pass it. Is there a similar way to define a generic object literal whose type parameter can be dynamically inferred based on its contents? Something like:

interface <T>GenericObj { 
  arr: Array<T>, 
  obj: Partial<T>
}

I am aware I can make the interface generic in the usual way like so:

interface GenericObj<T> { 
  arr: Array<T>, 
  obj: Partial<T>
}

but I want to avoid that, because then I would have to declare the generic type in advance whenever I am using the interface. For example,
const x: XYZ

will not work (unless I provide a default generic parameter type). If I want to make the declaration somewhat general, I am forced to write:
const x: XYZ<any>

but this does not allow TypeScript to dynamically infer the specific generic type based on the actual contents of x

@ghost
Copy link

ghost commented May 24, 2018

So basically, you want to write: const x: XYZ = { ... } and have the type argument to XYZ be inferred?

Note you can already write const x = { ... } and have a type inferred, though I wouldn't recommended because:

  • Excess properties won't be detected (we don't know you want this to be an XYZ, so we don't know which properties are necessary and which aren't). This is especially dangerous if XYZ has any optional properties that you might misspell.
  • String literals will be typed as string, but you might have needed a string literal type.
  • Any functions inside the object won't have a contextual type.

I think all of the above reasons apply just as well to const x: XYZ with no explicit type parameter. So it's not clear that there's a real need for a level of typing in between const x = { ... } and const x: XYZ<T> = { ... }.

@agyemanjp
Copy link
Author

agyemanjp commented May 24, 2018

Thanks for the comment.
Someone pointed out that this issue seems related to #17574, so looking at that may give a fuller explanation of what I am suggesting.

I think you make valid points, but in the case where a generic function type can be inferred, the same thing may be required for an object literal. Consider this snippet:

function reduce<T>(arr: T[], reducer: (first: T, second: T) => T): T {
    if (arr.length === 0) throw new Error()
    else if (arr.length === 1) return arr[0]
    else return reducer(arr[0], reduce(arr.slice(1), reducer))
}

let r = reduce(["x", "y"], (a, b) => a + b)

Here TypeScript is able to automatically infer the generic type of the reduce function call as string

But now consider the arguments of the reduce function expressed as a generic interface:

interface ReductionArgs<T> {
    arr: T[]
    reducer: (first: T, second: T) => T
}

Lets say we want to declare a object literal of keyed ReductionArgs objects, where each key value can have a different generic type. The best we can do, currently, is something like:

let reductionArgsDict: { [key: string]: ReductionArgs<any>}

But now we lost the generic type checking. We could of course explicitly add as ReductionArgs<SPECIFIC-TYPE> to the end each member of the dictionary, but that would mean coercing a generic type.

@ghost
Copy link

ghost commented May 24, 2018

That would be an existential type then? (#14466) exists T. ReductionArgs<T>

@mhegazy mhegazy added the Duplicate An existing issue was already created label May 25, 2018
@agyemanjp
Copy link
Author

agyemanjp commented May 30, 2018

I think so, in that example I don't care what the generic type is, so long as the object is internally consistent. But that is only one example; I think #17574 comes closer to a comprehensive solution

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants