-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Default generic type variables #2175
Comments
👍 It would greatly simplify definition for some lib, for example something like react components would ideally be : class Component<P,S,C> {
props: P;
state: S;
context:C
....
} but most of the time class Component<P = {}, S = {}, C = {}> {
props: P;
state: S;
context:C
....
} It would be easy to create simple component that don't use those generic types: class MyComponent extends Component {
}
// instead of
class MyComponent extends Component<{}, {}, {}>{
} |
Interesting idea, though it would seem that this functionality is easily achieved through a subclass. class Foo<T,U,V> {
// ...
}
// Or DefaultFoo or BasicFoo
class StringyFoo<U,V> extends Foo<string, U, V> {
// ...
} Are there any use cases where doing this becomes annoyingly difficult, or where it doesn't quite achieve the same thing? |
While that could be useful in some cases, I feel I wouldn't use that for these reasons:
In @fdecampredon 's example code with 3 generic variables it would be especially hard to cover all the use cases (some users might want to use just P, others just S, others just C, others P and S, etc.. |
I'm working on a library right now that could have really benefited from this. When combined with constraints, I'd expect the syntax to look something like the following:
|
+1. This would be great indeed when working with React. |
Btw, why is this labeled under "Needs Proposal" ? This is already present in C++, not in C# unfortunately. However I don't think this would be hard to implement? |
Someone needs to write down what all the rules for this would be. For example, would defaults be legal in all generic type parameter positions? If so, what's the behavior in function calls? declare function f<T = string>(a: T, x: (n: T) => void): void;
// What do we resolve 'T' to here? By what mechanism?
f(null, s => s.charCodeAt(0)); |
In addition to default type parameter, it would also be great to have named type argument. So I don't need to care about the order. class Component extends React.Component<P = T1, S = T2> {
} |
@RyanCavanaugh my first (maybe naive) idea for this was a simple preprocessor which would work like this:
So in the above example, because f is called without given an explicit type it will default to the default generic argument (string) and that's it. Do you see any problems with this approach? Is it that with a default value we ignore the possibility of type inference that would otherwise work just by looking at the types of the passed function parameters (without setting a generic type)?
Let me know if I'm missing something! |
@tinganho that sounds like a good idea! |
My suggestion would be that type defaulting happens in place of an
I find it more important on classes, however, where type inference may not be able to be performed.
As indicated in my example, I'd propose that, just like default (non-type) parameters in function calls, all type parameters after the first defaulted type parameter must have a default. I'd also suggest that type constraints are evaluated after all inference is complete, in case it matters for constraint checking. (I can't imagine a situation where it would, given type constraints cannot reference type parameters in the same type parameter list at this time.) I'd rather not discuss named type parameters, as that could be a separate feature entirely. (Open a separate issue, perhaps, @tinganho?) |
@mdekrey I agree, it would work well if the default type was only relevant for the Is there a use case for specifying both a default and |
@ander-nz I think there is a use case for that, to me those are independent features, I may want to use extend to make sure a type is a subclass of something, but I may also want to provide a default type. class ElementContainer<T = HTMLDivElement extends HTMLElement>
{
private _el: T;
constructor(el: T)
{
this._el = el;
el.style.color = "#ff0000";
}
}
// use:
var container: ElementContainer; Users of the class don't need to specify a type, it will default to div, but they can specify one if they want, they can also use type inference (by instantiation) and I can also make sure T is a HTMLElement so I can use code in the class that relies on that (eg.: ".style.color = ...") |
@andrewvarga That's a good example use case for both. While I agree that those are independent features, they do of course have some overlap that would need to be considered. |
Taking from @andrewvarga's example, I'd actually prefer that the default goes after the
As such, I guess my proposal becomes:
Is there anything else needed to make a formal proposal? |
I second @mdekrey's proposal, I was just looking for exactly this. |
Related issue: #209 - Generic Parameter Overloads |
It would also be very helpful to allow the default types to themselves be generic types. For example:
This way the second type defaults to being the same as the first, but the user can override this if desired. |
Discussed at the design backlog review today. How do people feel about the following proposal?
In code: interface Alpha<T extends HTMLElement> { x: T }
interface Beta<T> { x: T }
interface Gamma<T extends {}> { x: T }
var x: Alpha; // Equivalent to var x: Alpha<HTMLElement>
var y: Beta; // Error, type parameter T has no default (has no constraint)
var z: Gamma; // Equivalent to var z: Gamma<{}> |
Could you have a constraint also be a type variable?
|
Generic parameter defaults will make the implementation of #1213 and #9949 considerably harder. They seem challenging at presents, so it doesn't help. Defaults are a source of ambiguities as well. Adding a default may make a function backward incompatible. E.g. declare function c<A>(f: (x: A) => A, g: (a: A) => A): A;
declare function f<T = number>(x: T): T;
declare function g<T = string>(x: T): T;
c(f, g) // A = string / number or T? Better type inference and type aliases are more obvious and consistent solutions since defaults help only in the case envisioned by the library/typings author and are opaque to the code maintainers. The only true benefit of defaults is generifying existing typings (as pointed out by @blakeembrey's example) but I'd much rather see real generics first.. |
If nested types make it, then this would all of a sudden become far more interesting. You could make a React type namespace, so you could properly type the library, including raw element references, and ensure that React Native-specific components can't be erroneously used in DOM-specific or renderer-independent components. That being simply by using a generic React type instead. It's an extra layer of verification that would allow much more thorough typing.
That would be a good question, though. |
@RyanCavanaugh Are you guys planning to add a milestone to this guy? Looks like you had the spec (and implementation?) mostly fleshed out. |
Awesome, thanks for the excellent work @RyanCavanaugh! |
👍 Thanks. I've been wating a long time. |
It would be really useful if I could set default values for my generic type variables. In many cases, the users of my class don't want to use a different value and so it's cumbersome to having to type it in.
If I could do this:
then the people who don't care about the type of
userData
(because they don't use it) and the people who are fine with it being aString
don't have to set a generic type but others can if they want.Also, these type variables could default to
any
or the type they extend (if they do) without explicitly defaulting them.For example:
<T extends MyClass>
would default toMyClass
(without explicitly writing it)and
<T>
could default toany
What do you think?
The text was updated successfully, but these errors were encountered: