You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It would be good if the following statement were possible:
exporttypeMyApi=typeofmyApiFactory();
Explanation
Since the function is a factory rather than the actual service, this type alias would other components to have a strongly-typed reference to the service. Though this example relates to AngularJS' service factory and dependency injection, this particular change would not be solely for AngularJS support.
Existing solutions
One option to resolve this issue is to explicitly define an interface for the service. However, this solution would require repeating the signature for all of the service's methods.
A workaround exists that relies on JavaScript's scoping rules to satisfy TypeScript's typeof keyword. However, it significantly reduces readability and clarity. An example:
I have found myself wishing this were possible sometimes - usually when using type definitions from DefinitelyTyped. For example, knex.d.ts internally defines a QueryBuilder interface but does not export it, even though most knex methods return instances of this type. That makes it a pain to, say, write a function that takes a QueryBuilder as an argument.
A workaround is to create a 'dummy' variable with the desired type using an expression that is not necessarily evaluated, and then using typeof on that. In the knex case, the following works:
The proposal outlined by @ander-nz is far cleaner than this, although I agree with Mohamed Hegazy (@mhegazy) that putting expressions in type positions may be confusing.
The best approach in this example is to fix knex.d.ts and sumbit a PR to DefinitelyTyped, but that's a 'big fix' approach that may not always be possible, and for which typeof expr could provide a nice short-term fix in the meantime.
This would be equivalent of C++'s decltype(expr), which is used in a type position but does not evaluate expr. It's a very useful facility for writing reuseable code that propagates types without foreknowledge of their names (or shapes).
vara=typeofmyApiFunc();// actually invokes myApiFunc, and then sets a to "object".typeA=typeofmyApiFunc();// does not invoke it, only looks at its return type.
A possible alternate syntax is using <``> brackets, since these are already used by TypeScript. For example:
typeMyApi=typeof<myApiFunc()>;
This could simplify the syntax, and would look out of place enough to remind the reader that the expression is a TypeScript feature. Does this look clearer?
Mohamed Hegazy (@mhegazy) And to answer your question about how the type would be used outside the module. That example is designed for AngularJS, which uses dependency injection, such as:
If this facility existed as a purely compile-time thing, say __returnTypeof expr or decltype(expr) or whatever, could it be used in an ambient context? E.g:
declare module "mymodule"{importknex= require('knex');typeQueryBuilder=__returnTypeof(knex(<any>{})());// obtain the QueryBuilder type from knex;exportfunctionsomeExportedFunction(query: QueryBuilder): void;}
This covers a likely use case for the construct, but would involve allowing expressions to appear in ambient declarations. Is that feasible with the design of the compiler?
I think that the scope of typeof expression is very wide. It is very powerful but that also means that expressing some specific patterns in it would require sacrificing tidiness and clarity which would lead to lower general usability in those scenarios.
Perhaps it would be beneficial to try to isolate the most prominent cases where typeof expression would be commonly used (such as, say, typeon or returnTypeOf [and perhaps also returnTypeOn?] ) and try to individually consider a more "optimized", special-purpose syntax and semantics for each one of them.
It would still be useful to have a more powerful operator for special cases, however, it might be possible to emulate many of them by embedding the evaluated expression in a function and "extracting" the inferred return type, like this:
functionexpressionFunc(){return[...]// expression}// can also be written as 'expressionFunc = () => [..]'varx: returnTypeOfexpressionFunc;
This would naturally extend to generic expressions as well:
functionf(x: number,y: string){returnx||y;}typeT=(typeoff).ReturnType;// string | number
Every function type could have a .ReturnType member that would be equal to the type of what the function returns. Typescript knows this type, but doesn't seem to make it accessible in a straightforward way.
I think using a "virtual" sub-type/property is an interesting approach, and I'm also investigating alternatives to returnTypeOf and returnTypeOn myself (including even syntax that would handle overloaded return signatures etc.). The problem with this particular syntax is that Typescript (and Javascript) allows declaring hybrid function/object types (functions with normal properties defined on them):
interfaceHybridFunc{// This is a function signature specifier:(x: number,y: string): string;// This is an example of a normal property with type 'Array<boolean>'// that just happens to have the name 'ReturnType':ReturnType: Array<boolean>;}lethybridFunc: HybridFunc;
So in this case type type T = typeof hybridFunc.ReturnType would be ambiguous.
Using it with my proposed typeon operator, (an operator that would allow referencing a property type directly through its containing type): would also have the same problem: type T = typeon HybridFunc.ReturnType.
This workaround would only work on runtime code positions though, so it is mostly relevant totypeof func(). It can still be used to emulate typeon this.func() in some cases, but that would be more complex:
But similarly to other workarounds for typeon: in the case of a generic interface or class it would not be possible to propagate the generic parameter into the dummy instance:
interfaceA<T>{func(x: number): T;prop: typeonthis.func();// No workaround for this}
Basarat Ali Syed (@basarat)Rotem Dan (@rotemdan) At present typeof is useable in ambient contexts (e.g. in .d.ts files), and perhaps the extended typeof proposed here could preserve this quality. That's one thing the workarounds, consisting of imperative statements of various forms, cannot do.
Activity
lazdmx commentedon Aug 8, 2015
👍 Seems related to #3749 (comment) as means to get collection element type through expression.
mhegazy commentedon Aug 8, 2015
I find Using an expression with potential sides effects in a type position really confusing.
mhegazy commentedon Aug 8, 2015
Also can you elaborate on how the API and the type would be used outside the module?
yortus commentedon Aug 8, 2015
I have found myself wishing this were possible sometimes - usually when using type definitions from DefinitelyTyped. For example,
knex.d.tsinternally defines aQueryBuilderinterface but does not export it, even though mostknexmethods return instances of this type. That makes it a pain to, say, write a function that takes aQueryBuilderas an argument.A workaround is to create a 'dummy' variable with the desired type using an expression that is not necessarily evaluated, and then using
typeofon that. In theknexcase, the following works:The proposal outlined by @ander-nz is far cleaner than this, although I agree with Mohamed Hegazy (@mhegazy) that putting expressions in type positions may be confusing.
The best approach in this example is to fix
knex.d.tsand sumbit a PR to DefinitelyTyped, but that's a 'big fix' approach that may not always be possible, and for whichtypeof exprcould provide a nice short-term fix in the meantime.yortus commentedon Aug 8, 2015
This would be equivalent of C++'s
decltype(expr), which is used in a type position but does not evaluateexpr. It's a very useful facility for writing reuseable code that propagates types without foreknowledge of their names (or shapes).Andael commentedon Aug 9, 2015
Mohamed Hegazy (@mhegazy) I agree that the syntax could be confusing, as per below:
A possible alternate syntax is using
<``>brackets, since these are already used by TypeScript. For example:This could simplify the syntax, and would look out of place enough to remind the reader that the expression is a TypeScript feature. Does this look clearer?
Andael commentedon Aug 9, 2015
Mohamed Hegazy (@mhegazy) And to answer your question about how the type would be used outside the module. That example is designed for AngularJS, which uses dependency injection, such as:
mhegazy commentedon Aug 9, 2015
i would rather have a compiler operator e.g.
__returnTypeofthan to overloadtypeof.yortus commentedon Aug 9, 2015
If this facility existed as a purely compile-time thing, say
__returnTypeof exprordecltype(expr)or whatever, could it be used in an ambient context? E.g:This covers a likely use case for the construct, but would involve allowing expressions to appear in ambient declarations. Is that feasible with the design of the compiler?
rotemdan commentedon Sep 10, 2015
I think that the scope of
typeof expressionis very wide. It is very powerful but that also means that expressing some specific patterns in it would require sacrificing tidiness and clarity which would lead to lower general usability in those scenarios.Perhaps it would be beneficial to try to isolate the most prominent cases where
typeof expressionwould be commonly used (such as, say,typeonorreturnTypeOf[and perhaps alsoreturnTypeOn?] ) and try to individually consider a more "optimized", special-purpose syntax and semantics for each one of them.It would still be useful to have a more powerful operator for special cases, however, it might be possible to emulate many of them by embedding the evaluated expression in a function and "extracting" the inferred return type, like this:
This would naturally extend to generic expressions as well:
benliddicott commentedon Sep 10, 2015
Can I add my vote to this item. This seems very useful and has wide application.
d180cf commentedon Sep 11, 2015
Why not to introduce the notion of subtypes?
Every function type could have a .ReturnType member that would be equal to the type of what the function returns. Typescript knows this type, but doesn't seem to make it accessible in a straightforward way.
rotemdan commentedon Sep 11, 2015
d180cf
I think using a "virtual" sub-type/property is an interesting approach, and I'm also investigating alternatives to
returnTypeOfandreturnTypeOnmyself (including even syntax that would handle overloaded return signatures etc.). The problem with this particular syntax is that Typescript (and Javascript) allows declaring hybrid function/object types (functions with normal properties defined on them):So in this case type
type T = typeof hybridFunc.ReturnTypewould be ambiguous.Using it with my proposed
typeonoperator, (an operator that would allow referencing a property type directly through its containing type): would also have the same problem:type T = typeon HybridFunc.ReturnType.basarat commentedon Sep 14, 2015
Going though the issue. I suggest that a simpler workaround be used:
Basically instead of
You have two lines:
rotemdan commentedon Sep 14, 2015
Basarat Ali Syed (@basarat)
I've found an even simpler workaround yesterday:
To get return types:
This workaround would only work on runtime code positions though, so it is mostly relevant to
typeof func(). It can still be used to emulatetypeon this.func()in some cases, but that would be more complex:But similarly to other workarounds for
typeon: in the case of a generic interface or class it would not be possible to propagate the generic parameter into the dummy instance:benliddicott commentedon Sep 14, 2015
See also Generic Meta Types #3779
yortus commentedon Sep 14, 2015
Basarat Ali Syed (@basarat) Rotem Dan (@rotemdan) At present
typeofis useable in ambient contexts (e.g. in.d.tsfiles), and perhaps the extendedtypeofproposed here could preserve this quality. That's one thing the workarounds, consisting of imperative statements of various forms, cannot do.msklvsk commentedon Dec 19, 2015
Dear Santa, please bring me
decltypein Typescript.yortus commentedon Jan 25, 2016
I submitted a proposal + implementation for this: #6606
RyanCavanaugh commentedon Jan 25, 2016
Let's take the discussion over to the nice clean slate at #6606.
magnushiie commentedon Jun 16, 2017
With recent null checks, the above recommendation:
can be changed to
to avoid the (undefined | ...) type.