Reputation: 9778
I have a function wrap
that can take either a T
or a Wrapped<T>
and returns a Wrapped<T>
.
Declared in TS 1.0:
wrap<T>(value: Wrapped<T>, defaultValue?: T): Wrapped<T>;
wrap<T>(value: T, defaultValue?: T): Wrapped<T>;
Now in 1.4, I want to use union types to define the concept of "either a T
or a Wrapped<T>
"
class Bar {
thing: Wrapped<number>;
constructor(thing: number | Wrapped<number>) {
this.thing = wrap(thing);
}
}
But the type checker errors on the this.thing
assingment with:
Type Wrapped<number | Wrapped<number>> is not assignable to Wrapped<number>
Type 'number | Wrapped<number>' is not assignable to type number
I understand that the error is that the value I'm passing to wrap
is number | Wrapped<number>
and wrap
only knows how to handle number
and Wrapped<number>
;
So my first try was to go declare that it can handle T | Wrapped<T>
;
wrap<T>(value: T | Wrapped<T>, defaultValue?: T): Wrapped<T>;
wrap<T>(value: Wrapped<T>, defaultValue?: T): Wrapped<T>;
wrap<T>(value: T, defaultValue?: T): Wrapped<T>;
But the type checker still errors. If I right click and go to definition I see that it's still binding to the third definition in the list rather than to my earlier, higher-precedence signature for T | Wrapped<T>
.
Is this the expected behavior?
Am I supposed to resolve this manual with type guards? If so, could someone show me how to do it with this generic Wrapper that I have? How do I get a type parameter from my thing
variable?
Upvotes: 0
Views: 79
Reputation: 7246
Simply combine the overloaded functions:
class Wrapped<T> {
private _value: T;
get value(): T { return this._value; }
constructor(value: T) {
this._value = value;
}
}
class WrappHelper {
public static wrap<T>(value: Wrapped<T> | T, defaultValue ?: T): Wrapped<T> {
if (value instanceof Wrapped)
return value;
else
return new Wrapped<T>(<T>value);
}
}
var a = new Wrapped(100);
var b = 200;
var a1: Wrapped<number> = WrappHelper.wrap(a);
var a2: Wrapped<number> = WrappHelper.wrap(b);
Upvotes: 1
Reputation: 220944
This compiles:
interface Wrapped<T> { value: T }
declare function wrap<T>(value: T|Wrapped<T>, defaultValue?: T): Wrapped<T>;
class Bar {
thing: Wrapped<number>;
constructor(thing: number | Wrapped<number>) {
this.thing = wrap(thing); // No error
}
}
General guidance for choosing between a union type and a function of two overloads is that if you can write it as a single-signature union type, you should.
Upvotes: 1