Luke Joshua Park
Luke Joshua Park

Reputation: 9805

Is there a TypeScript compiler option to force type checks on object properties?

In order to write more robust code, I'm looking at using TypeScript for my web based projects. I have relatively little experience with the language thus far, but I have run into a problem that I'm having difficulty searching for.

Consider the TypeScript code below:

public static getResponseCode(resObj: object): number {
    let c: number = resObj["c"]
    return c;
}

Is there a way that I can force the TypeScript compiler to pull me up here and tell me that resObj["c"] may not be a number and that I need to first check if it is a number? E.g. can I force TypeScript to make me rewrite the code as this (or something similar)?:

public static getResponseCode(resObj: object): number {
    if (typeof resObj["c"] !== "number") { return APIResponseCode.UnknownFailure; }

    let c: number = resObj["c"]
    return c;
}

I expect I'm getting the results that I am simply because the type of resObj["c"] is any. What can I do here? Is there a common pattern used in TypeScript?

Upvotes: 3

Views: 3279

Answers (2)

user663031
user663031

Reputation:

TypeScript does no run-time type-checking. Depending on what you are trying to do, you have a couple of options. The first is to define an interface for resObj:

interface ResObj { c: number; }

Now

public static getResponseCode(resObj: ResObj): number {
  let c: number = resObj.c;
  return c;
}

Your code will not compile now if you pass getResponseCode something that does not match ResObj (with a c property which is not a number).

What if you want c to be maybe a number, or maybe a string? In that case, use a union type:

interface ResObj { c: number | string; }

Your original version of getResponseCode above will now fail to compile. TypeScript will complain that it does not know how to convert string | number into number.

If you write the code as you proposed:

public static getResponseCode(resObj: ResObj): number {
  if (typeof resObj.c !== "number") { return APIResponseCode.UnknownFailure; }

  let c: number = resObj.c;
  return c;
}

TypeScript will be happy, since its flow analysis detects the typeof check and knows that by the time it reaches the return statement c can only be a number.

For more complex situations, you can use what is called a type guard, which tells TypeScript explicitly that a particular function guarantees a particular type of its parameter:

function isNumber(c: string | number): c is number {
  return typeof c === 'number';
}

Now you can write

public static getResponseCode(resObj: ResObj): number {
  if (!isNumber(resObj.c)) { return APIResponseCode.UnknownFailure; }

  let c: number = resObj.c;
  return c;
}

Upvotes: 2

Shaun Luttin
Shaun Luttin

Reputation: 141512

You can turn on the noImplicitAny compiler option. The compiler will then complain any time something is implicitly any.

We set noImplictAny inside tsconfig.json.

{
    "compilerOptions": {
        "noImplicitAny": true
    }
}

Or we set it like this tsc --noImplicitAny from the command line.

That would force you to do something.

For instance, you could use any explicitly.

public static getResponseCode(resObj: object): number {
    const resAny = resObj as any;
    if (resAny.c && typeof resAny.c !== "number") {
        return APIResponseCode.UnknownFailure;
    }

    const c: number = resAny.c;
    return c;
}

Or you could use an interface with a union type and a type guard.

interface ResObj {
    c: boolean | string | number | object;
}

public static getResponseCode2(resObj: ResObj): number {

    if (typeof resObj.c !== "number") {
        return APIResponseCode.UnknownFailure;
    }

    const c: number = resObj.c;
    return c;
}

Upvotes: 2

Related Questions