Reputation: 12809
I have a TypeScript method which accepts a parameter. The parameter can be either of type string
or type Object
. When I use a typeof
check for type string
in the method body, I still get error stating that indexOf
and substr
are not found on type string | Object
.
As far as I understand the if
block body should be type-guarded by the condition. What am I doing wrong?
function mergeUrlParameters(sourceParameters: string | Object, targetUrl: string, overwrite: boolean = false): string {
var sourceParamObject: Object;
if (typeof sourceParameters === "string") {
if (sourceParameters.indexOf("?") >= 0)
sourceParameters = sourceParameters.substr(sourceParameters.indexOf("?") + 1);
sourceParamObject = $.deparam(sourceParameters, true);
} else {
sourceParamObject = sourceParameters;
}
...
}
I'm using Visual Studio 2015 with latest TypeScript installed (1.8.x).
UPDATE
It turned out that the reassignment of the sourceParameters
variable causes the type-guard to fail. I've posted a related potential bug report about this behavior here. In the meantime, several easy workarounds are available, I chose to inline the check and substring call with a ?
conditional expression.
Upvotes: 2
Views: 1638
Reputation: 164347
You need to cast it to the appropriate type:
function mergeUrlParameters(sourceParameters: string | Object, targetUrl: string, overwrite: boolean = false): string {
var sourceParamObject: Object;
if (typeof sourceParameters === "string") {
if ((<string> sourceParameters).indexOf("?") >= 0) {
sourceParameters = (<string> sourceParameters).substr((<string> sourceParameters).indexOf("?") + 1);
}
sourceParamObject = $.deparam((<string> sourceParameters), true);
} else {
sourceParamObject = sourceParameters;
}
...
}
If you want to avoid that casting, you can create your own type guard (which you haven't in your code):
function isString(x: any): x is string {
return typeof x === "string";
}
and then:
function mergeUrlParameters(sourceParameters: string | Object, targetUrl: string, overwrite: boolean = false): string {
var sourceParamObject: Object;
if (isString(sourceParameters)) {
if (sourceParameters.indexOf("?") >= 0) {
sourceParameters = sourceParameters.substr(sourceParameters.indexOf("?") + 1);
}
sourceParamObject = $.deparam(sourceParameters, true);
} else {
sourceParamObject = sourceParameters;
}
...
}
more info in TypeScript Handbook | typeof type guards
Ok, after playing around with it for a while I understand what is the compiler problem with your code.
This variation of your code compiles with no errors:
function mergeUrlParameters(sourceParameters: string | Object, targetUrl: string, overwrite: boolean = false): string {
var sourceParamObject: Object;
if (typeof sourceParameters === "string") {
sourceParamObject = $.deparam(sourceParameters.indexOf("?") >= 0 ?
sourceParameters.substr(sourceParameters.indexOf("?") + 1)
: sourceParameters
, true);
} else {
sourceParamObject = sourceParameters;
}
}
The main difference being that in your code you reassign a value to sourceParameters
and that probably confuses the compiler.
To illustrate the issue, consider this simple example:
function x(param: number | string): number {
var num: number;
if (typeof param === "string") {
num = parseInt(param);
} else {
num = param;
}
return num;
}
function y(param: number | string): number {
if (typeof param === "string") {
param = parseInt(param);
}
return param;
}
function x
compiles without errors, but y
has the following error:
Argument of type 'number | string' is not assignable to parameter of type 'string'. Type 'number' is not assignable to type 'string'
(check it out in the playground)
Upvotes: 1