Repository navigation
Make the type-level typeof aware of generic arguments #28931
Description
Activity
canonic-epicure commented
on Dec 9, 2018 AuthorMore actionsI'd expect this to compile:
type T<V> = typeof valueProducer<V> // TS2304: Cannot find name 'V'. // or type T<V> = (typeof valueProducer)<V>
and I'd expect
T<V>to be exact asValueProducer<V>- changed the title
[-]Make `typeof` aware of generic arguments[/-][+]Make type-level `typeof` aware of generic arguments[/+]on Dec 9, 2018 - changed the title
[-]Make type-level `typeof` aware of generic arguments[/-][+]Make the type-level `typeof` aware of generic arguments[/+]on Dec 9, 2018 canonic-epicure commented
on Dec 9, 2018 AuthorMore actionsOr, I'd expect:
type T = typeof valueProducer // Error - Type `T` is not generic type T<A> = typeof valueProducer // Compiles fine, `A` is bound to `V` in `valueProducer`
calebsander commented
on Dec 10, 2018 ContributorMore actionsThe two types are actually different. The way you have declared
valueProducer, you are saying it is a function which takes in a parameter of any type and returns the same type as the parameter. (This is what<V>(i?: V) => Vmeans). However,ValueProducer<V>is a function which takes in a parameter of one specific type (V) and returnsV. Just asValueProduceris a meaningless type without the generic argument,typeof valueProduceris not a generic type and so it is meaningless to give it a generic argument.Reacted by Daniel Rosenwasser and Nathan FennerReacted by Daniel Rosenwasser- addedWorking as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
on Dec 10, 2018 canonic-epicure commented
on Dec 10, 2018 AuthorMore actionsCaleb Sander Mateos (@calebsander) Exactly, I propose to actually make the type-level
typeofto return the "real", "open" generic types in such cases.Otherwise there's an inconsistency in the language. You are saying that
valueProduceris a function which takes in a parameter of any type and returns the same type as the parameter.
In other words, its a "normal" type, not a generic. Yet it is possible to provide a type argument for the
valueProducerduring the call:valueProducer<Date>(new Date())Why is it so, if the type of
valueProducerdoes not have any type arguments? Special case? Exception from rules? Why, if its possible to just maketypeofto be aware of generic arguments.jack-williams commented
on Dec 10, 2018 CollaboratorMore actionsI think this is related: #17574
canonic-epicure commented
on Dec 11, 2018 AuthorMore actionsJack Williams (@jack-williams) it is, yes, though the scope of the latter is broader than this ticket. Using the terminology from #17574, it can be formulated as:
function id<V> (v : V) { return v } type Id2<V> = (v: V) => V type Id3 = <V>(v: V) => V type TypeOfId = typeof id
Which one of the Id2 and Id3 should be returned by
typeof id? Currently it isId3and I think the correct answer isId2, because you can provide a type argument for theidduring the call.jack-williams commented
on Dec 11, 2018 CollaboratorMore actionsWhich one of the Id2 and Id3 should be returned by typeof id? Currently it is Id3 and I think the correct answer is Id2, because you can provide a type argument for the id during the call.
The type of
idshould beId3because values should be represented by types like<V>(v: V) => V, not functions from types to types.Id2andId3really are not the same. The first one just introduces a type name for sharing in the type graph, the second denotes something that is genuinely parametric in type. Just because I can write:type Id<V> = (v: V) => V const f: Id<boolean> = (x: boolean) => !x; // not parametric
this does not mean that f is, or ever was, parametric in the type of its argument. However the following f is, hence why I can instantiate it freely.
type Id<V> = (v: V) => V const f: Id<boolean> = <X>(x: X) => x; // parametric
Following your suggestion to return
Id2would actually be weakening the type because you end up "forgetting" that the value was ever actually parametric. For instance, I could not do this.type NumberPicker = (picker: <X>(x: X, y: X) => X, x: number) => number const constantPicker1 = <X>(x: X, y: X) => x; const constantPicker2 = <X>(x: X, y: X) => y; declare const x: typeof constantPicker1; declare const y: typeof constantPicker2; const pick: NumberPicker = (picker, x) => picker(x, -x); // ok as x and y are parametric pick(x, 3); pick(y, 4);
I think what you really want is to separate type application from function application, so it can be done partially, and then have that syntax in type queries. Like:
type T<V> = (typeof valueProducer<V>)
canonic-epicure commented
on Dec 12, 2018 AuthorMore actionsI think what you really want is to separate type application from function application, so it can be done partially, and then have that syntax in type queries. Like:
type T<V> = (typeof valueProducer<V>)
This will work for my purposes, yes! Any chance for this to be implemented?
typescript-bot commented
on Dec 14, 2018 ContributorMore actionsThis issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
canonic-epicure commented
on Dec 18, 2018 AuthorMore actionsJack Williams (@jack-williams) FYI, I've filed the feature request for solution you suggested: #29043
It seems to be small, non-breaking and very useful feature, hope to see it implemented.
Reacted by Jack Williams- locked as resolved and limited conversation to collaborators
on Oct 21, 2025
The
typeofon type-level is not aware of the generic type arguments. Consider:Intuitively,
valueProducerhas type ofValueProducer:valueProducer : ValueProducer, however, when we do:we get this type back instead
The latter type is not generic, as there's no "free" type variables in it.
In other words, the
valueProducer<V>is an "open" generic type, that can be specialized by a single type argument. But thetypeof valueProduceris a "closed" generic type, that can not be specialized.