I am attempting to create a ternary expression and I am getting the following error
"Type of conditional expression cannot be determined because there is no implicit conversion between LiveSubscription and DisconnectedSubscription"
The same logic works in an if statement, but I wanted to understand why it won't work in a ternary expression -
Here is the gist of what I am trying to do:
public interface IClientSubscription
{
bool TryDisconnect();
}
public class LiveSubscription : IClientSubscription
{
public bool TryDisconnect()
{
return true;
}
}
public class DisconnectedSubscription : IClientSubscription
{
public bool TryDisconnect()
{
return true;
}
}
public class ConnectionManager
{
public readonly IClientSubscription Subscription;
public ConnectionManager(bool IsLive)
{
// This throws the exception
Subscription = (IsLive)
? new LiveSubscription()
: new DisconnectedSubscription();
// This works
if (IsLive)
{
Subscription = new LiveSubscription();
}
else
{
Subscription = new DisconnectedSubscription();
}
}
}
I could always switch it to an if/else but I wanted to understand what is going wrong first!
You need to cast at least one of the operands to IClientSubscription:
Subscription = (IsLive)
? (IClientSubscription)new LiveSubscription()
: new DisconnectedSubscription();
The reason is that the ternary expression is of a certain type which is determined by the operands. Basically, it tries to cast the second operand to the type of the first or vice versa. Both fail here, because LiveSubscription isn't an DisconnectedSubscription and vice versa.
The compiler doesn't check whether both share a common base type.
Trying to answer your question in the comment:
No, ternary expressions are not some sort of object, but a ternary expression is the right hand part of an assignment. Each right hand part expression of an assignment has a certain type, otherwise it would be impossible to assign this expression to the variable on the left hand side.
Examples:
var x = Guid.NewGuid()
The right hand side expression (Guid.NewGuid()) is of type Guid, because the method NewGuid() returns a Guid.
var x = y.SomeMethod()
The right hand side expression is of the type of the return type of SomeMethod().
var x = IsLive ? "a" : 1
This is obviously invalid, isn't it? What type should x be? A string or an int?
This would lead to the exact same error message that you had with your code.
Your example a bit changed:
var subscription = (IsLive) ? new LiveSubscription()
: new DisconnectedSubscription();
Note the var before subscription, we now initialize a new variable, not an existing. I think even here, it is obvious what the problem is: What type should subscription be? LiveSubscription or DisconnectedSubscription? It can be neither, because depending on IsLive it needs to be either the one or the other.
About the comparison with if:
In your code where you assign a new LiveSubscription instance or a new DisconnectedSubscription instance to Subscription an implicit cast to IClientSubscription is occurring, because the compiler knows that Subscription is of type IClientSubscription and both LiveSubscription and DisconnectedSubscription can implicitly be converted to that interface.
The assignment with the ternary expression is a bit different, because the compiler first tries to evaluate the ternary expression and only afterwards it tries to assign it to Subscription. This means that the compiler doesn't know that the result of the ternary expression needs to be of type IClientSubscription.
Related
I'm still quite new to typescript, so please be gentle with me if I'm doing something with no sense for this technology!
The problem that I'm trying to solve is having a dynamic way to define how my application errors should be structured, but leaving to the users the faculty to enrich the messages.
So I tried to create this logic in a module that could be extended easily from the application, but I'm currently facing the problem:
Error:(35, 18) TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'ErrorMessage' has no compatible call signatures.
What I thought it was a good idea (but please tell me if I'm wrong), was to use a register and a map to have the possibility to extend this mapping every time I want. So I created my ErrorMessage interface to be like the following:
export interface ErrorMessage {
actionMessage: string;
actionSubject: string;
originalErrorMessage?: string;
toString: () => string;
}
and a register for these, called ErrorResponseRegister, as it follows:
export enum defaultErrors {
ExceptionA = 'ExceptionA',
ExceptionB = 'ExceptionB',
}
export class ErrorResponseRegister {
private mapping: Map<string, ErrorMessage>;
constructor() {
this.mapping = new Map()
.set(defaultErrors.ExceptionA, exceptionAErrorMessage)
.set(defaultErrors.ExceptionB, exceptionBErrorMessage);
}
}
So at the end, every ErrorMessage function should look like:
export function exceptionAErrorMessage(originalErrorMessage?: string): ErrorMessage {
return {
enrichment1: "Something happened",
enrichment2: "in the application core",
originalErrorMessage: originalErrorMessage,
toString(): string {
return `${this.enrichment1} ${this.enrichment2}. Original error message: ${originalErrorMessage}`;
},
};
}
Please note I haven't used classes for this ones, as it doesn't really need to be instantiated
and I can have a bunch of them where the toString() method can vary. I just want to enforce the errors should have an enrichment1 and enrichment2 that highlight the problem in a better way for not-technical people.
So, now, back to code. When I'm trying to use the exceptionAErrorMessage statically, I can't see any problem:
console.log(exceptionAErrorMessage(originalErrorMessage).toString())
But when I try dynamically, using the map defined in the ErrorResponseRegister, something weird happens:
// In ErrorResponseRegister
public buildFor(errorType: string, originalErrorMessage?: string): Error {
const errorMessageBuilder = this.mapping.get(errorType);
if (errorMessageBuilder) {
return errorMessageBuilder(originalErrorMessage).toString();
}
return "undefined - do something else";
}
The code works as expected, the error returned is in the right format, so the toString function is executed correctly.
BUT, the following error appears in the IDE:
Error:(32, 18) TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'ErrorMessage' has no compatible call signatures.
The line that causes the problem is
errorMessageBuilder(originalPosErrorMessage).toString()
Can someone help me to understand what I'm doing wrong?
It looks like your problem is you've mistyped mapping... it doesn't hold ErrorMessage values; it holds (x?: string)=>ErrorMessage values:
private mapping: Map<string, (x?: string) => ErrorMessage>;
What's unfortunate is that you initialize this variable via new Map().set(...) instead of the using an iterable constructor argument.
The former returns a Map<any, any> which is trivially assignable to mapping despite the mistyping. That is, you ran smack into this known issue where the standard library's typings for the no-argument Map constructor signature produces Map<any, any> which suppresses all kinds of otherwise useful error messages. Perhaps that will be fixed one day, but for now I'd suggest instead that you use the iterable constructor argument, whose type signature declaration will infer reasonable types for the keys/values:
constructor() {
this.mapping = new Map([
[defaultErrors.ExceptionA, exceptionAErrorMessage],
[defaultErrors.ExceptionB, exceptionBErrorMessage]
]); // inferred as Map<defaultErrors, (orig?: string)=>ErrorMessage>
}
If you had done so, it would have flagged the assignment as an error with your original typing for mapping (e.g., Type 'Map<defaultErrors, (originalErrorMessage?: string | undefined) => ErrorMessage>' is not assignable to type 'Map<string, ErrorMessage>'.) Oh well!
Once you make those changes, things should behave more reasonably for you. Hope that helps; good luck!
Link to code
I'm trying to write my own boolean "abstract" with some additional functions.
#forward
abstract MyBool(Bool) {
public inline function new(b:Bool) {
this = b;
}
#:from
public static inline function fromBool(b:Bool):MyBool {
return new MyBool(b);
}
#:to
public inline function toBool():Bool {
return this;
}
// some additional functions
}
In principal this works fine:
var t:T = true;
if(t) {
trace("1");
}
t.someStrangeMethod();
However #:forward does not forward basic boolean-operators like "!":
var f:T = false;
if(!f) { // fails here, because "!" is not defined as an operator for MyBool ...
trace("2");
}
The error message is "MyBool should be Bool", which I find quite strange because MyBool is an abstract of a Bool with #:forward annotation and there is a #:to-method.
Of course there are some easy workarounds. One could either use:
if(!f.toBool()) {
trace("2");
}
and/or add a function annotated with #:op(!A) to the abstract:
#:op(!A)
public inline function notOp():Bool {
return !this;
}
However I do not like both methods:
I dislike adding #:op(...) to MyBool, because creating a method for each possible operator would require much code (Maybe not with a boolean, but e.g. with an Int, Float, ...).
I dislike using !var.toBool(). If someone has already written quite some code (s)he does not want to go through all of it, when (s)he simply wants to change Bool to a MyBool ... I mean of course (s)he could also cast Bool to MyBool whenever adding new code, but that can be horrible too.
So I was wondering if anyone has a better idea? Is there maybe another "#:forward"-like compiling metadata, I do not know about yet?
There's an open feature request regarding this:
Can #:forward also forward underlying operator overloads? (#5035)
One way to make your code example work is to allow implicit conversions with to Bool. I'm not entirely sure why the equivalent #:to function doesn't work here, as the Haxe Manual states that "Class field casts have the same semantics".
abstract MyBool(Bool) to Bool {
Apart from that, I think the only options is to declare an #:op function for each operator you want to support. If declared without a body, the underlying type's operator will be forwarded:
#:op(!A) function notOp():MyBool;
If your main goal is to just add methods to the Bool type, then perhaps avoid the problem altogether by instead creating a class that adds methods to Bool via static extension (documented in the Haxe manual). This method would eliminate the need for operator forwarding.
I have a function,
public static IPagedResponse<T> GetPagedResponse<T, TAnon>(
this IQueryable<TAnon> query,
QueryableRequestMessage request)
where T : class
{
//...
}
I'm trying to pass query as an IQueryable of an anonymous type.
var query = _repository.All.Select(
i => new //anon type
{
i.Id,
i.Name,
}
);
var result = query.GetPagedResponse<EftInterfaceDto, ??????>(request);
The issue is I don't know what to put in place of ??????? It can't seem to infer it. And any combinations using .GetType() or typeof() I have tried, failed.
I tried changing the function to be IQueryable<dynamic> but that resulted in other errors, about dynamic not being allowed in Expression trees.
HACK:
I can make it work if I change my function to this:
public static IPagedResponse<T> GetPagedResponse<T, TAnon>(
this IQueryable<TAnon> query,
QueryableRequestMessage request,
T typeSample)
where T : class
{
//...
}
And then pass in an instance of T
var result = query.GetPagedResponse(request, new SomeClassOfT());
This way, I can use type inference to determine the anonymous type TAnon, and don't need to be explicit in the call to the generic (no <types> required).
However, I don't want to do this, as it's clearly not clear what I'm doing.
I grabbed System.Linq.Dynamic.DynamicQueryable from here:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
The issue that I am running into is in code that looks like this:
var results = dataContext.GetTable<MyClass>.Select("new (MyClassID, Name, Description)").Take(5);
It appears that if that line of code is executed by multiple threads near simultaneously, Microsoft's dynamic Linq code crashes in their ClassFactory.GetDynamicClass() method, which looks like this:
public Type GetDynamicClass(IEnumerable<DynamicProperty> properties)
{
rwLock.AcquireReaderLock(Timeout.Infinite);
try
{
Signature signature = new Signature(properties);
Type type;
if (!classes.TryGetValue(signature, out type))
{
type = CreateDynamicClass(signature.properties);
classes.Add(signature, type); // <-- crashes over here!
}
return type;
}
finally
{
rwLock.ReleaseReaderLock();
}
}
The crash is a simple dictionary error: "An item with the same key has already been added."
In Ms code, The rwLock variable is a ReadWriterLock class, but it does nothing to block multiple threads from getting inside classes.TryGetValue() if statement, so clearly, the Add will fail.
I can replicate this error pretty easily in any code that creates a two or more threads that try to execute the Select("new") statement.
Anyways, I'm wondering if anyone else has run into this issue, and if there are fixes or workarounds I can implement.
Thanks.
I did the following (requires .NET 4 or later to use System.Collections.Concurrent):
changed the classes field to a ConcurrentDictionary<Signature, Type> ,
removed all the ReaderWriterLock rwLock field and all the code referring to it,
updated GetDynamicClass to:
public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) {
var signature = new Signature(properties);
return classes.GetOrAdd(signature, sig => CreateDynamicClass(sig.properties));
}
removed the classCount field and updated CreateDynamicClass to use classes.Count instead:
Type CreateDynamicClass(DynamicProperty[] properties) {
string typeName = "DynamicClass" + Guid.NewGuid().ToString("N");
...
I am running into a problem when trying to implicitly convert one of my dynamic types. There are two assemblies with definitions similar to the following:
Configuration.dll:
public class ConfigurationValue : DynamicObject
{
public ConfigurationValue(string val)
{
//...
}
//...
public static implicit operator string(ConfigurationValue val)
{
return val.ToString();
}
}
There is another class in this dll called Configuration with a member variable called Instance (to make the class singleton). This variable holds the ConfigurationValue instances in a dictionary and is of type dynamic. This allows me to do this following:
Server.dll:
//...
if (Configuration.Instance.SecurityLevel != "Insecure")
{
//...
}
Assuming that SecurityLevel is in the dictionary.
This if statement appears verbatim in my code and always fails with the following error:
{"Operator '!=' cannot be applied to operands of type 'System.Dynamic.DynamicObject' and 'string'"}
Previously, when these two classes were in the same assembly, this code worked fine. Can anyone tell me what I'm doing wrong here?
Thanks,
Max
Solved the problem, a little embarrassing actually, I forgot to change the container class for ConfigurationValue (e.g. the type of Configuration.Instance) from internal to public when I moved it to the new assembly, so of course the type couldn't be resolved and the implicit conversion was not found
Try
var SecurityLevel = new ConfigurationValue("Insecure");