Home > Blockchain >  Why does typescript fail to assign values to multiple string literal union types but works with stri
Why does typescript fail to assign values to multiple string literal union types but works with stri

Time:02-01

I am rather new to typescript and are trying to assign the correct types to my JS files. While doing so, I am trying to write as view boilerplate as possible and so I stumbled over this:

I receive compilation errors only under specific conditions. As soon as I call the array filter method after line 14, it starts failing to compile but doesn't fail at line 17.

Why does it fail when calling filter and why does it not fail in line 17?

interface A {
  iAm: 'a';
}
  
interface B {
  iAm: 'b';
}

interface C {
  iAm: string;
}


const fails: (A|B)[] = [{iAm: 'a'}, {iAm: 'b'}]
  .filter(() => true);

const works: (B|C)[] = [{iAm: 'b'}, {iAm: 'c'}]
  .filter(() => true);

Fiddle

Error:

const fails: (A | B)[]
Type '{ iAm: string; }[]' is not assignable to type '(A | B)[]'.
  Type '{ iAm: string; }' is not assignable to type 'A | B'.
    Type '{ iAm: string; }' is not assignable to type 'B'.
      Types of property 'iAm' are incompatible.
        Type 'string' is not assignable to type '"b"'.(2322)

CodePudding user response:

This is because typescript is inferring {iAm: 'a'} as type {iAm: string}.

To tell typescript that you mean the literal string 'a' you can use "as const".

Your code example will become.

interface A {
  iAm: 'a';
}
  
interface B {
  iAm: 'b';
}

interface C {
  iAm: string;
}


const fails: (A|B)[] = [{iAm: 'a'}, {iAm: 'b'}]
  .filter((x) => {
    return true
  });

const failsButNowWorks: (A|B)[] = [{iAm: 'a' as const}, {iAm: 'b' as const}]
  .filter((x) => {
    return true
  });


const works: (B|C)[] = [{iAm: 'b'}, {iAm: 'c'}]
  .filter(() => true);

I believe it is the .filter that is causing this conversion since you can technically modify the value in a .filter

For example

const works: (B|C)[] = [{iAm: 'b'}, {iAm: 'c'}]
  .filter((x) => {
    x.iAm = 'asdf'
    return true
  });

But if you use "as const" it will infer it correctly and cause a type error.

const failsButNowWorks: (A|B)[] = [{iAm: 'a' as const}, {iAm: 'b' as const}]
  .filter((x) => {
// Type Error here since iAm now has type `{iAm: 'a' | 'b' }`
    x.iAm = 'asdf'
    return true
  });
  • Related