'Why object value can not assign to an union type in TypeScript

Say, if I have two union type in TypeScript, for example:

const a = 1;
const b = 2;

type AB = typeof a | typeof b;
type AABB = 'aa' | 'bb';

type Test = AB | AABB;

const obj = {
  a: 'aa',
  b: 'bb'
}

const t1: Test = 'aa';
const t2: Test = obj.a;

'aa' can assign to t1, but obj.a can not assign to t2, why?



Solution 1:[1]

Because your obj's type is:

const obj: {
    a: string;
    b: string;
}

The object properties are interpreted by TS to be just strings, not specific strings. So obj.a is a string, but a Test is a (union of) specific strings, so it's not assignable.

Change to

const obj = {
  a: 'aa',
  b: 'bb'
} as const;

so that the properties aren't widened, and your code will work as expected.

Solution 2:[2]

Just to add on to the answer from @CertainPerformance, as const is called a const assertion, and makes the typescript compiler interpet all literals with in object as their narrowed, readonly types.

To explain, for your case:

const obj = {
  a: 'aa',
  b: 'bb'
};

type Inferred = {
  a: string,
  b: string
};

Alternatively:

const obj = {
  a: 'aa',
  b: 'bb'
} as const;

type Inferred = {
  readonly a: 'aa',
  readonly b: 'bb'
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 CertainPerformance
Solution 2 Hunter Kohler