chinesedfan
chinesedfan

Reputation: 439

What is the detail logic of type assertion in typescript?

I treat type assertion as something like Hi Compiler, I know the type of this variable better than you. Just follow me!.

But it seems that the compiler still has its own logic to infer the type. For example, suppose,

interface PM_MGEInfo {
    category: string;
    bid: string;
    cid?: string;
    labs?: { [key: string]: any };
}

Then, 1&2 are no problem, but 3 throws a TS2352 error.

  1. function makeMgeInfo(bid: string): PM_MGEInfo {
        return <PM_MGEInfo>{
            bid
        };
    }
    
  2. function makeMgeInfo(bid: string): PM_MGEInfo {
        return <PM_MGEInfo>{
            bid,
            labs: {}
        };
    }
    
  3. function makeMgeInfo(bid: string): PM_MGEInfo {
        return <PM_MGEInfo>{
            bid,
            // error TS2352: Type '{ labs: { poi_id: string; }; bid: string; }' cannot be converted to type 'PM_MGEInfo'.
            // Property 'category' is missing in type '{ labs: { poi_id: string; }; bid: string; }'.
            labs: {a: 1}
        };
    }
    

Why does type assertion begin to check other fields in 3? Does someone know its detail logic?


Updated: I created an issue in Github Microsoft/TypeScript#23698.

Upvotes: 5

Views: 183

Answers (3)

chinesedfan
chinesedfan

Reputation: 439

Check the spec 4.16 Type Assertions, which is inspired by this answer:

In a type assertion expression of the form < T > e, e is contextually typed (section 4.23) by T and the resulting type of e is required to be assignable to T, or T is required to be assignable to the widened form of the resulting type of e, or otherwise a compile-time error occurs.

For case 1, T is assignable to e obviously.

For case 2, the widened form of e is {bid: string, labs: Object}, which T is assignable to. Notice that labs? is assignable to Object (In fact, I am not sure about it, but this is my only possible explanation).

For case 3, neither of above conditions is satisfied.

Upvotes: 1

ochs.tobi
ochs.tobi

Reputation: 3454

I think that the compiler doesn't recognize your value {a: 1} as type { [key: string]: any } because if you alter the line like this there is no compiler error:

function makeMgeInfo(bid: string): PM_MGEInfo {
  return <PM_MGEInfo>{
    bid,
    labs: {a: 1} as {[key: string]: any}
  };
}

also it is working if you define the variable like this:

mylabs: {[key: string]: any} = {a: 1};

and call it in your function:

makeMgeInfo(bid: string): PM_MGEInfo {
  return <PM_MGEInfo>{
    bid,
    labs: this.mylabs
  };
}

Therefore I would say, that the compiler does not recognize your defined type from the labs field. The error message in this case is very misleading.

Upvotes: 0

kctang
kctang

Reputation: 11202

I see two options. Either use Partial<T> or any. When using any, YOU are responsible for making sure things will work, so use as last option.

Examples of both:

function makeMgeInfoWithPartial(bid: string): Partial<PM_MGEInfo> {
  // props are optional but only those defined by PM_MGEInfo are valid
  return {
    bid,
    labs: {a: 1}
  }
}

function makeMgeInfoWithAny(bid: string): PM_MGEInfo {
  return {
    bid,
    labs: {a: 1},
    whatever: 'also works'
  } as any
}

Upvotes: 0

Related Questions