-
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
How to define a parameter as optional, but if specified be ensured as not undefined. #11988
Comments
There isn't a way to specify that a parameter is optional but may not accept |
There seems to be other ways of solving this but it seems like extra work (like in my sample). Here's an example of how I ran into this and what I had to do to get around it... // Given the following map function... And the Selector as described above.
function map<T,TResult>(source:T[], s:Selector<T,TResult>):TResult[] {
var result:TResult[] = [];
for(let i = 0; i<source.length; i++) {
let e = source[i];
result[i] = s(e,i);
}
return result;
}
console.log(map(["a","b","c"], (e,i)=>i)); // [0,1,2] The problem I encountered is when you use The only solution I came up with (which is okay, but not ideal) is adding an overload to this example map method: function map<T,TResult>(source:T[], s:(e:T,i:number)=>TResult):TResult[]
function map<T,TResult>(source:T[], s:Selector<T,TResult>):TResult[]
{
///....
} And to be honest I'm not 100% sure if the above code works. Actual implementation (of what I did) is here: |
Callback parameters (e.g. |
Right... That actually makes sense. I think on a fundamental level you are correct. The issue is the amount of additional code required to get it to work that way. So this comes back to (please help me if I'm doing this wrong): Why can't I: export interface Selector<TSource, TResult>
{
(source:TSource):TResult;
(source:TSource, index:number):TResult;
} When I do the above, I encounter a huge amount of breakage. |
And so more importantly... I'm pretty confident I can get the above to work... BUT... |
Okay so here's the problem I'm seeing with the above signature...
Which they didn't have any problem inferring the type of |
So it keeps falling back to (as you were saying) being more constrained (not optional parameters) but that I have to define multiple signatures for every method that takes a selector. |
One last question: a(v:Number) {}
a(v:String) {}
a(v:Number | String) {}
// older way:
b(v:Number) {}
b(v:String) {}
b(v:any) {} But in the case of 'b' you aren't allowed to pass boolean even though the root is 'any'. :/ In the case I'm talking about... Why is this allowed: a(s:Selector<T,TResult>) {} // where Selector does not contain an index parameter.
a(s:SelectorWithIndex<T,TResult>) {} ...update... a(s:Selector<T,TResult>|SelectorWithIndex<T,TResult>) {}
a(s:SelectorWithIndex<T,TResult>) {} |
Please take questions to Stack Overflow. I think you also need to refer to this FAQ entry: https://github.com/Microsoft/TypeScript/wiki/FAQ#why-am-i-getting-supplied-parameters-do-not-match-any-signature-error regarding signatures, and also remember not to use In general if you have a callback type, it should only have one signature, and you should specify the full arity of that signature. If you have a function type, write as few signatures as possible (i.e. combine individual types into union types), and place more-specific signatures (those taking more arguments or more-specific types) before less-specific signatures. |
Sorry if I'm over asking. It still seems like something that needs fixing. I have to write a lot more to get the result I'm expecting.. Including (update) needing to do this: a(s:Selector<T,TResult>) {}
a(s:SelectorWithIndex<T,TResult>) {}
a(s:SelectorWithIndex<T,TResult>) {} |
I'm guessing that answers my question. It has to have a root signature. :/ |
This is very confusing because you're not even posting legal syntax. Self-contained examples, that actually parse, with what you expect to happen, would be really useful. |
I working on a solution and will post it and close this issue once I do (shortly). |
Okay here's the update. And please hang in here with me because I do believe there is a problem that when I try an implement this, it breaks type inference. export interface SelectorWithoutIndex<TSource, TResult>
{
(source:TSource):TResult;
}
export interface SelectorWithIndex<TSource, TResult>
{
(source:TSource, index:number):TResult;
}
export type Selector<TSource, TResult>
= SelectorWithoutIndex<TSource,TResult>
| SelectorWithIndex<TSource, TResult>;
export interface ActionWithoutIndex<T> extends SelectorWithoutIndex<T, void>
{
}
export interface ActionWithIndex<T> extends SelectorWithIndex<T, void>
{
}
export type Action<T>
= ActionWithoutIndex<T>
| ActionWithIndex<T>;
export interface PredicateWithoutIndex<T> extends SelectorWithoutIndex<T, boolean>
{
}
export interface PredicateWithIndex<T> extends SelectorWithIndex<T, boolean>
{
}
export type Predicate<T>
= PredicateWithoutIndex<T>
| PredicateWithIndex<T>;
export interface Comparison<T>
{
(a:T, b:T, strict?:boolean):number;
}
export interface EqualityComparison<T>
{
(a:T, b:T, strict?:boolean):boolean;
}
export interface Func<TResult>
{
():TResult;
}
export interface Closure
{
():void;
} By using the above code (eliminating optional parameters) I find a lot of serious issues with my own code and THANK YOU for strictNullChecks... AMAZING. Can't say enough about this feature. But... The above changes break inference all over the place... forEach(action:Action<T> | Predicate<T>):void { } Now to make it work I have to: forEach(action:ActionWithIndex<T>):void;
forEach(action:ActionWithoutIndex<T>):void;
forEach(action:PredicateWithIndex<T>):void;
forEach(action:PredicateWithoutIndex<T>):void;
forEach(action:PredicateWithIndex<T> | ActionWithIndex<T>):void {} Otherwise I get
|
So I've confirmed that type inference is unnecessarily broken. All methods that appears as such: a(p:Predicate<T>) {} Have to be rewritten to look like: a(p:PredicateWithoutIndex<T>):void;
a(p:PredicateWithIndex<T>):void;
a(p:PredicateWithIndex<T>):void {} |
Again you have a ton of overcomplicated code here but no actual invocations that show what behavior you're trying to accomplish. |
Ok. I admit, it's a bit complicated, and unclear. I apologize. Here is the code before I've migrated away from optional parameters (which I see now as essential for me to do). And here is an actual use case that if not implemented correctly (using signatures) will end up with the type inference error I described: I originally spotted this issue here: I could give you more examples, but the list would be long. |
In the end. It is still possible to do what I need to do. But I think there could be an improvement in type inference because it really seems odd that I have to write so much additional code in order to get it to understand my intention. I'm now in the middle of rewriting a significant amount of type-code to get the signatures and constraints right so that:
|
Before you spend a ton of time on this, I just want to be clear: You should be able to define a signature |
Sure that might solve that particular case. But the problem really boils down to some very strange type inference issue. Where I'm quite happy to have 2 separate signatures. One for just You're welcome to watch me work on this: (@RyanCavanaugh I mention your name in the video but I slip and say Dave :P. Sorry.) |
@RyanCavanaugh just an update. I may have figured this out. Wanted to say thanks again for your help. |
This is a bit tough to explain but probably similar to: #10014
TypeScript Version: 2.0.6
Code
So if I want to specify (when using strict null checks):
Which will allow me to use functions that take 1 or 2 parameters where the second one is a number or undefined. But what I really want is that if a second parameter is defined it will be a number.
Any signature style overrides I tried seemed to just screw things up more.
Is there a workaround for this without having to create separate interfaces like:
The text was updated successfully, but these errors were encountered: