0

In Functional languages, I can define a method where the signature clearly says that either the method returns Type A or Type B. ex:

class AppError(message: String)
public convertToString(input: Int) : Either[AppError, String]

The left side is always the un-happy path, and right side is the happy path. I am trying to do something similar in typescript. I wrote this code

type AppError = {message: String, errorCode: number}
function doSomething(param1: number) : AppError | string {
  if (param1 < 0) {
    return {"message": "param1 cannot be less than 0"}
  } else {
    return +param1
  }
}
const result3 = doSomething(-1)
if (typeof result3 === "AppError") {
  console.log("got this exception" + result3.message)
} else 
  console.log("got result " + result3)
}

But the typescript compiler says

This condition will always return 'false' since the types '"string" | "number" | "bigint" | 
"boolean" | "symbol" | "undefined" | "object" | "function"' and '"AppError"' have no 
overlap.ts(2367)

I googled and I found this thread but I still don't understand why will typeof x always be false? My code is returning the AppError object if you pass a negative number and in that case, the typeof result3 === 'AppError' should evaluate to true.

1 Answer 1

1

typeof in JavaScript and TypeScript does not return a class name or constructor name, but it only returns the native JavaScript value-type-name, which is only one of:

'string'
'number'
'bigint'
'boolean'
'symbol'
'undefined'
'object'        // Note that `typeof aNullVar` evalutes to `'object'` btw.
'function'

What you want is a TypeScript type guard function, which is used for runtime type checking that the compiler will "trust" to enforce type-safety.

Note how the return-type of isAppError below is value is AppError.

Like so:

function isAppError( value: string | AppError ): value is AppError {
    return typeof value === 'object'
        && ( value !== null )
        && ( 'message' in value )
        && ( 'errorCode' in value )
        && ( typeof (value as AppError).errorCode === 'number' );
}

This isAppError function can probably be simplified (e.g. you don't need the value !=== null check if you're in strict-mode.

And used like so:

if( isAppError( result3 ) ) {
    // TypeScript *knows* that `result3` is of-type `AppError` inside this `if` statement's scope.

    console.log( result3.errorCode ); // TypeScript knows that result3 is AppError here.
}
else {
    // If `result3`'s type is an intersection type of only 2 types then TypeScript *knows& that result3 is of-type `string` inside this `else` statement's scope.

    console.log( result3.substring(1) );  // TypeScript knows that result3 is string here.
}

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.