-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Suggestion: Using typeof with an expression #4233
Comments
👍 Seems related to #3749 (comment) as means to get collection element type through expression. |
I find Using an expression with potential sides effects in a type position really confusing. |
Also can you elaborate on how the API and the type would be used outside the module? |
I have found myself wishing this were possible sometimes - usually when using type definitions from DefinitelyTyped. For example, A workaround is to create a 'dummy' variable with the desired type using an expression that is not necessarily evaluated, and then using import Knex = require('knex');
var dummyQueryBuilder = false ? Knex(<any>{})() : null;
type QueryBuilder = typeof dummyQueryBuilder; The proposal outlined by @ander-nz is far cleaner than this, although I agree with @mhegazy that putting expressions in type positions may be confusing. The best approach in this example is to fix |
This would be equivalent of C++'s |
@mhegazy I agree that the syntax could be confusing, as per below: var a = typeof myApiFunc(); // actually invokes myApiFunc, and then sets a to "object".
type A = typeof myApiFunc(); // does not invoke it, only looks at its return type. A possible alternate syntax is using type MyApi = 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? |
@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: function MyCtrl($scope, myApi: myApp.MyApi) {
// ...
} |
i would rather have a compiler operator e.g. |
If this facility existed as a purely compile-time thing, say declare module "mymodule" {
import knex = require('knex');
type QueryBuilder = __returnTypeof (knex(<any>{})()); // obtain the QueryBuilder type from knex;
export function someExportedFunction(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 Perhaps it would be beneficial to try to isolate the most prominent cases where 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: function expressionFunc() {
return [...] // expression
}
// can also be written as 'expressionFunc = () => [..]'
var x: returnTypeOf expressionFunc; This would naturally extend to generic expressions as well: function genericExpressionFunc<T>() {
return [...] // generic expression
}
var x: returnTypeOf expressionFunc<string>; |
Can I add my vote to this item. This seems very useful and has wide application. |
Why not to introduce the notion of subtypes? function f(x: number, y: string) {
return x || y;
}
type T = (typeof f).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 interface HybridFunc {
// 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>;
}
let hybridFunc: HybridFunc; So in this case type Using it with my proposed |
Going though the issue. I suggest that a simpler workaround be used: function myApiFactory() {
return {
getSetting: () => null
};
}
const myApiFactoryReturn = false && myApiFactory();
export type MyApi = typeof myApiFactoryReturn; Basically instead of export type MyApi = typeof myApiFactory(); You have two lines: const myApiFactoryReturn = false && myApiFactory();
type MyApi = typeof myApiFactoryReturn; |
I've found an even simpler workaround yesterday: // This expression would never execute but its type would be inferred:
let e = true ? undefined : ...expression...;
type ExpressionType = typeof e; To get return types: let r = true ? undefined : someFunction();
type RerturnType = typeof r; This workaround would only work on runtime code positions though, so it is mostly relevant to interface A {
func(x: number): string;
prop: ReturnType; // emulates 'typeon this.func()'
}
let a: A;
let r = true ? undefined : a.func(0);
type RerturnType = typeof r; But similarly to other workarounds for interface A<T> {
func(x: number): T;
prop: typeon this.func(); // No workaround for this
} |
See also Generic Meta Types #3779 |
Dear Santa, please bring me |
I submitted a proposal + implementation for this: #6606 |
Let's take the discussion over to the nice clean slate at #6606. |
With recent null checks, the above recommendation:
can be changed to
to avoid the (undefined | ...) type. |
It would be helpful if the
typeof
keyword were changed to support more complex expressions, such as function calls.Example
Given the following AngularJS service definition:
It would be good if the following statement were possible:
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:The text was updated successfully, but these errors were encountered: